source "sound/soc/au1x/Kconfig"
source "sound/soc/bcm/Kconfig"
source "sound/soc/cirrus/Kconfig"
-source "sound/soc/davinci/Kconfig"
source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig"
source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
-source "sound/soc/omap/Kconfig"
source "sound/soc/kirkwood/Kconfig"
source "sound/soc/img/Kconfig"
source "sound/soc/intel/Kconfig"
source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
+source "sound/soc/ti/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/uniphier/Kconfig"
source "sound/soc/ux500/Kconfig"
obj-$(CONFIG_SND_SOC) += au1x/
obj-$(CONFIG_SND_SOC) += bcm/
obj-$(CONFIG_SND_SOC) += cirrus/
-obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
obj-$(CONFIG_SND_SOC) += hisilicon/
obj-$(CONFIG_SND_SOC) += meson/
obj-$(CONFIG_SND_SOC) += mxs/
obj-$(CONFIG_SND_SOC) += nuc900/
-obj-$(CONFIG_SND_SOC) += omap/
obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/
obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
+obj-$(CONFIG_SND_SOC) += ti/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += uniphier/
obj-$(CONFIG_SND_SOC) += ux500/
+++ /dev/null
-config SND_DAVINCI_SOC
- tristate
- depends on ARCH_DAVINCI
- select SND_EDMA_SOC
-
-config SND_EDMA_SOC
- tristate "SoC Audio for Texas Instruments chips using eDMA"
- depends on TI_EDMA
- select SND_SOC_GENERIC_DMAENGINE_PCM
- help
- Say Y or M here if you want audio support for TI SoC which uses eDMA.
- The following line of SoCs are supported by this platform driver:
- - daVinci devices
- - AM335x
- - AM437x/AM438x
- - DRA7xx family
-
-config SND_DAVINCI_SOC_I2S
- tristate "DaVinci Multichannel Buffered Serial Port (McBSP) support"
- depends on SND_EDMA_SOC
- help
- Say Y or M here if you want to have support for McBSP IP found in
- Texas Instruments DaVinci DA850 SoCs.
-
-config SND_DAVINCI_SOC_MCASP
- tristate "Multichannel Audio Serial Port (McASP) support"
- depends on SND_SDMA_SOC || SND_EDMA_SOC
- help
- Say Y or M here if you want to have support for McASP IP found in
- various Texas Instruments SoCs like:
- - daVinci devices
- - Sitara line of SoCs (AM335x, AM438x, etc)
- - DRA7x devices
-
-config SND_DAVINCI_SOC_VCIF
- tristate
-
-config SND_DAVINCI_SOC_GENERIC_EVM
- tristate
- select SND_SOC_TLV320AIC3X
- select SND_DAVINCI_SOC_MCASP
-
-config SND_AM33XX_SOC_EVM
- tristate "SoC Audio for the AM33XX chip based boards"
- depends on SND_EDMA_SOC && SOC_AM33XX && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
- help
- Say Y or M if you want to add support for SoC audio on AM33XX
- boards using McASP and TLV320AIC3X codec. For example AM335X-EVM,
- AM335X-EVMSK, and BeagelBone with AudioCape boards have this
- setup.
-
-config SND_DAVINCI_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
- depends on SND_EDMA_SOC && I2C
- depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
- select SND_DAVINCI_SOC_GENERIC_EVM
- help
- Say Y if you want to add support for SoC audio on TI
- DaVinci DM6446, DM355 or DM365 EVM platforms.
-
-choice
- prompt "DM365 codec select"
- depends on SND_DAVINCI_SOC_EVM
- depends on MACH_DAVINCI_DM365_EVM
-
-config SND_DM365_AIC3X_CODEC
- tristate "Audio Codec - AIC3101"
- help
- Say Y if you want to add support for AIC3101 audio codec
-
-config SND_DM365_VOICE_CODEC
- tristate "Voice Codec - CQ93VC"
- select MFD_DAVINCI_VOICECODEC
- select SND_DAVINCI_SOC_VCIF
- select SND_SOC_CQ0093VC
- help
- Say Y if you want to add support for SoC On-chip voice codec
-endchoice
-
-config SND_DM6467_SOC_EVM
- tristate "SoC Audio support for DaVinci DM6467 EVM"
- depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
- select SND_SOC_SPDIF
-
- help
- Say Y if you want to add support for SoC audio on TI
-
-config SND_DA830_SOC_EVM
- tristate "SoC Audio support for DA830/OMAP-L137 EVM"
- depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
-
- help
- Say Y if you want to add support for SoC audio on TI
- DA830/OMAP-L137 EVM
-
-config SND_DA850_SOC_EVM
- tristate "SoC Audio support for DA850/OMAP-L138 EVM"
- depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C
- select SND_DAVINCI_SOC_GENERIC_EVM
- help
- Say Y if you want to add support for SoC audio on TI
- DA850/OMAP-L138 EVM
-
+++ /dev/null
-# SPDX-License-Identifier: GPL-2.0
-# DAVINCI Platform Support
-snd-soc-edma-objs := edma-pcm.o
-snd-soc-davinci-i2s-objs := davinci-i2s.o
-snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
-snd-soc-davinci-vcif-objs:= davinci-vcif.o
-
-obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o
-obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
-obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
-obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
-
-# Generic DAVINCI/AM33xx Machine Support
-snd-soc-evm-objs := davinci-evm.o
-
-obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o
+++ /dev/null
-/*
- * ASoC driver for TI DAVINCI EVM platform
- *
- * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <linux/of_platform.h>
-#include <linux/clk.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-#include <asm/mach-types.h>
-
-struct snd_soc_card_drvdata_davinci {
- struct clk *mclk;
- unsigned sysclk;
-};
-
-static int evm_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *soc_card = rtd->card;
- struct snd_soc_card_drvdata_davinci *drvdata =
- snd_soc_card_get_drvdata(soc_card);
-
- if (drvdata->mclk)
- return clk_prepare_enable(drvdata->mclk);
-
- return 0;
-}
-
-static void evm_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *soc_card = rtd->card;
- struct snd_soc_card_drvdata_davinci *drvdata =
- snd_soc_card_get_drvdata(soc_card);
-
- if (drvdata->mclk)
- clk_disable_unprepare(drvdata->mclk);
-}
-
-static int evm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_card *soc_card = rtd->card;
- int ret = 0;
- unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
- snd_soc_card_get_drvdata(soc_card))->sysclk;
-
- /* set the codec system clock */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- /* set the CPU system clock */
- ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops evm_ops = {
- .startup = evm_startup,
- .shutdown = evm_shutdown,
- .hw_params = evm_hw_params,
-};
-
-/* davinci-evm machine dapm widgets */
-static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-/* davinci-evm machine audio_mapnections to the codec pins */
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Headphone connected to HPLOUT, HPROUT */
- {"Headphone Jack", NULL, "HPLOUT"},
- {"Headphone Jack", NULL, "HPROUT"},
-
- /* Line Out connected to LLOUT, RLOUT */
- {"Line Out", NULL, "LLOUT"},
- {"Line Out", NULL, "RLOUT"},
-
- /* Mic connected to (MIC3L | MIC3R) */
- {"MIC3L", NULL, "Mic Bias"},
- {"MIC3R", NULL, "Mic Bias"},
- {"Mic Bias", NULL, "Mic Jack"},
-
- /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
- {"LINE1L", NULL, "Line In"},
- {"LINE2L", NULL, "Line In"},
- {"LINE1R", NULL, "Line In"},
- {"LINE2R", NULL, "Line In"},
-};
-
-/* Logic for a aic3x as connected on a davinci-evm */
-static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- struct device_node *np = card->dev->of_node;
- int ret;
-
- /* Add davinci-evm specific widgets */
- snd_soc_dapm_new_controls(&card->dapm, aic3x_dapm_widgets,
- ARRAY_SIZE(aic3x_dapm_widgets));
-
- if (np) {
- ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
- if (ret)
- return ret;
- } else {
- /* Set up davinci-evm specific audio path audio_map */
- snd_soc_dapm_add_routes(&card->dapm, audio_map,
- ARRAY_SIZE(audio_map));
- }
-
- /* not connected */
- snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT");
- snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM");
- snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM");
-
- return 0;
-}
-
-/* davinci-evm digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link dm6446_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcbsp",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-001b",
- .platform_name = "davinci-mcbsp",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static struct snd_soc_dai_link dm355_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcbsp.1",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-001b",
- .platform_name = "davinci-mcbsp.1",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static struct snd_soc_dai_link dm365_evm_dai = {
-#ifdef CONFIG_SND_DM365_AIC3X_CODEC
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcbsp",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-mcbsp",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-#elif defined(CONFIG_SND_DM365_VOICE_CODEC)
- .name = "Voice Codec - CQ93VC",
- .stream_name = "CQ93",
- .cpu_dai_name = "davinci-vcif",
- .codec_dai_name = "cq93vc-hifi",
- .codec_name = "cq93vc-codec",
- .platform_name = "davinci-vcif",
-#endif
-};
-
-static struct snd_soc_dai_link dm6467_evm_dai[] = {
- {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name= "davinci-mcasp.0",
- .codec_dai_name = "tlv320aic3x-hifi",
- .platform_name = "davinci-mcasp.0",
- .codec_name = "tlv320aic3x-codec.0-001a",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
- },
- {
- .name = "McASP",
- .stream_name = "spdif",
- .cpu_dai_name= "davinci-mcasp.1",
- .codec_dai_name = "dit-hifi",
- .codec_name = "spdif_dit",
- .platform_name = "davinci-mcasp.1",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
- },
-};
-
-static struct snd_soc_dai_link da830_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name = "davinci-mcasp.1",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-mcasp.1",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static struct snd_soc_dai_link da850_evm_dai = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .cpu_dai_name= "davinci-mcasp.0",
- .codec_dai_name = "tlv320aic3x-hifi",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .platform_name = "davinci-mcasp.0",
- .init = evm_aic3x_init,
- .ops = &evm_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-/* davinci dm6446 evm audio machine driver */
-/*
- * ASP0 in DM6446 EVM is clocked by U55, as configured by
- * board-dm644x-evm.c using GPIOs from U18. There are six
- * options; here we "know" we use a 48 KHz sample rate.
- */
-static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = {
- .sysclk = 12288000,
-};
-
-static struct snd_soc_card dm6446_snd_soc_card_evm = {
- .name = "DaVinci DM6446 EVM",
- .owner = THIS_MODULE,
- .dai_link = &dm6446_evm_dai,
- .num_links = 1,
- .drvdata = &dm6446_snd_soc_card_drvdata,
-};
-
-/* davinci dm355 evm audio machine driver */
-/* ASP1 on DM355 EVM is clocked by an external oscillator */
-static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = {
- .sysclk = 27000000,
-};
-
-static struct snd_soc_card dm355_snd_soc_card_evm = {
- .name = "DaVinci DM355 EVM",
- .owner = THIS_MODULE,
- .dai_link = &dm355_evm_dai,
- .num_links = 1,
- .drvdata = &dm355_snd_soc_card_drvdata,
-};
-
-/* davinci dm365 evm audio machine driver */
-static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = {
- .sysclk = 27000000,
-};
-
-static struct snd_soc_card dm365_snd_soc_card_evm = {
- .name = "DaVinci DM365 EVM",
- .owner = THIS_MODULE,
- .dai_link = &dm365_evm_dai,
- .num_links = 1,
- .drvdata = &dm365_snd_soc_card_drvdata,
-};
-
-/* davinci dm6467 evm audio machine driver */
-static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = {
- .sysclk = 27000000,
-};
-
-static struct snd_soc_card dm6467_snd_soc_card_evm = {
- .name = "DaVinci DM6467 EVM",
- .owner = THIS_MODULE,
- .dai_link = dm6467_evm_dai,
- .num_links = ARRAY_SIZE(dm6467_evm_dai),
- .drvdata = &dm6467_snd_soc_card_drvdata,
-};
-
-static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = {
- .sysclk = 24576000,
-};
-
-static struct snd_soc_card da830_snd_soc_card = {
- .name = "DA830/OMAP-L137 EVM",
- .owner = THIS_MODULE,
- .dai_link = &da830_evm_dai,
- .num_links = 1,
- .drvdata = &da830_snd_soc_card_drvdata,
-};
-
-static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = {
- .sysclk = 24576000,
-};
-
-static struct snd_soc_card da850_snd_soc_card = {
- .name = "DA850/OMAP-L138 EVM",
- .owner = THIS_MODULE,
- .dai_link = &da850_evm_dai,
- .num_links = 1,
- .drvdata = &da850_snd_soc_card_drvdata,
-};
-
-#if defined(CONFIG_OF)
-
-/*
- * The struct is used as place holder. It will be completely
- * filled with data from dt node.
- */
-static struct snd_soc_dai_link evm_dai_tlv320aic3x = {
- .name = "TLV320AIC3X",
- .stream_name = "AIC3X",
- .codec_dai_name = "tlv320aic3x-hifi",
- .ops = &evm_ops,
- .init = evm_aic3x_init,
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_IB_NF,
-};
-
-static const struct of_device_id davinci_evm_dt_ids[] = {
- {
- .compatible = "ti,da830-evm-audio",
- .data = (void *) &evm_dai_tlv320aic3x,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids);
-
-/* davinci evm audio machine driver */
-static struct snd_soc_card evm_soc_card = {
- .owner = THIS_MODULE,
- .num_links = 1,
-};
-
-static int davinci_evm_probe(struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- const struct of_device_id *match;
- struct snd_soc_dai_link *dai;
- struct snd_soc_card_drvdata_davinci *drvdata = NULL;
- struct clk *mclk;
- int ret = 0;
-
- match = of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "Error: No device match found\n");
- return -ENODEV;
- }
-
- dai = (struct snd_soc_dai_link *) match->data;
-
- evm_soc_card.dai_link = dai;
-
- dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0);
- if (!dai->codec_of_node)
- return -EINVAL;
-
- dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0);
- if (!dai->cpu_of_node)
- return -EINVAL;
-
- dai->platform_of_node = dai->cpu_of_node;
-
- evm_soc_card.dev = &pdev->dev;
- ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model");
- if (ret)
- return ret;
-
- mclk = devm_clk_get(&pdev->dev, "mclk");
- if (PTR_ERR(mclk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (IS_ERR(mclk)) {
- dev_dbg(&pdev->dev, "mclk not found.\n");
- mclk = NULL;
- }
-
- drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
- if (!drvdata)
- return -ENOMEM;
-
- drvdata->mclk = mclk;
-
- ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
-
- if (ret < 0) {
- if (!drvdata->mclk) {
- dev_err(&pdev->dev,
- "No clock or clock rate defined.\n");
- return -EINVAL;
- }
- drvdata->sysclk = clk_get_rate(drvdata->mclk);
- } else if (drvdata->mclk) {
- unsigned int requestd_rate = drvdata->sysclk;
- clk_set_rate(drvdata->mclk, drvdata->sysclk);
- drvdata->sysclk = clk_get_rate(drvdata->mclk);
- if (drvdata->sysclk != requestd_rate)
- dev_warn(&pdev->dev,
- "Could not get requested rate %u using %u.\n",
- requestd_rate, drvdata->sysclk);
- }
-
- snd_soc_card_set_drvdata(&evm_soc_card, drvdata);
- ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card);
-
- if (ret)
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-
- return ret;
-}
-
-static struct platform_driver davinci_evm_driver = {
- .probe = davinci_evm_probe,
- .driver = {
- .name = "davinci_evm",
- .pm = &snd_soc_pm_ops,
- .of_match_table = of_match_ptr(davinci_evm_dt_ids),
- },
-};
-#endif
-
-static struct platform_device *evm_snd_device;
-
-static int __init evm_init(void)
-{
- struct snd_soc_card *evm_snd_dev_data;
- int index;
- int ret;
-
- /*
- * If dtb is there, the devices will be created dynamically.
- * Only register platfrom driver structure.
- */
-#if defined(CONFIG_OF)
- if (of_have_populated_dt())
- return platform_driver_register(&davinci_evm_driver);
-#endif
-
- if (machine_is_davinci_evm()) {
- evm_snd_dev_data = &dm6446_snd_soc_card_evm;
- index = 0;
- } else if (machine_is_davinci_dm355_evm()) {
- evm_snd_dev_data = &dm355_snd_soc_card_evm;
- index = 1;
- } else if (machine_is_davinci_dm365_evm()) {
- evm_snd_dev_data = &dm365_snd_soc_card_evm;
- index = 0;
- } else if (machine_is_davinci_dm6467_evm()) {
- evm_snd_dev_data = &dm6467_snd_soc_card_evm;
- index = 0;
- } else if (machine_is_davinci_da830_evm()) {
- evm_snd_dev_data = &da830_snd_soc_card;
- index = 1;
- } else if (machine_is_davinci_da850_evm()) {
- evm_snd_dev_data = &da850_snd_soc_card;
- index = 0;
- } else
- return -EINVAL;
-
- evm_snd_device = platform_device_alloc("soc-audio", index);
- if (!evm_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
- ret = platform_device_add(evm_snd_device);
- if (ret)
- platform_device_put(evm_snd_device);
-
- return ret;
-}
-
-static void __exit evm_exit(void)
-{
-#if defined(CONFIG_OF)
- if (of_have_populated_dt()) {
- platform_driver_unregister(&davinci_evm_driver);
- return;
- }
-#endif
-
- platform_device_unregister(evm_snd_device);
-}
-
-module_init(evm_init);
-module_exit(evm_exit);
-
-MODULE_AUTHOR("Vladimir Barinov");
-MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
- *
- * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- *
- * DT support (c) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
- * based on davinci-mcasp.c DT support
- *
- * 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.
- *
- * TODO:
- * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/platform_data/davinci_asp.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "edma-pcm.h"
-#include "davinci-i2s.h"
-
-#define DRV_NAME "davinci-i2s"
-
-/*
- * NOTE: terminology here is confusing.
- *
- * - This driver supports the "Audio Serial Port" (ASP),
- * found on dm6446, dm355, and other DaVinci chips.
- *
- * - But it labels it a "Multi-channel Buffered Serial Port"
- * (McBSP) as on older chips like the dm642 ... which was
- * backward-compatible, possibly explaining that confusion.
- *
- * - OMAP chips have a controller called McBSP, which is
- * incompatible with the DaVinci flavor of McBSP.
- *
- * - Newer DaVinci chips have a controller called McASP,
- * incompatible with ASP and with either McBSP.
- *
- * In short: this uses ASP to implement I2S, not McBSP.
- * And it won't be the only DaVinci implemention of I2S.
- */
-#define DAVINCI_MCBSP_DRR_REG 0x00
-#define DAVINCI_MCBSP_DXR_REG 0x04
-#define DAVINCI_MCBSP_SPCR_REG 0x08
-#define DAVINCI_MCBSP_RCR_REG 0x0c
-#define DAVINCI_MCBSP_XCR_REG 0x10
-#define DAVINCI_MCBSP_SRGR_REG 0x14
-#define DAVINCI_MCBSP_PCR_REG 0x24
-
-#define DAVINCI_MCBSP_SPCR_RRST (1 << 0)
-#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4)
-#define DAVINCI_MCBSP_SPCR_XRST (1 << 16)
-#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20)
-#define DAVINCI_MCBSP_SPCR_GRST (1 << 22)
-#define DAVINCI_MCBSP_SPCR_FRST (1 << 23)
-#define DAVINCI_MCBSP_SPCR_FREE (1 << 25)
-
-#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
-#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
-#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
-#define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
-#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
-#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24)
-#define DAVINCI_MCBSP_RCR_RPHASE BIT(31)
-
-#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
-#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
-#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
-#define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
-#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
-#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24)
-#define DAVINCI_MCBSP_XCR_XPHASE BIT(31)
-
-#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
-#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
-#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
-#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29)
-
-#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
-#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
-#define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
-#define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
-#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7)
-#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
-#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
-#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
-#define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
-
-enum {
- DAVINCI_MCBSP_WORD_8 = 0,
- DAVINCI_MCBSP_WORD_12,
- DAVINCI_MCBSP_WORD_16,
- DAVINCI_MCBSP_WORD_20,
- DAVINCI_MCBSP_WORD_24,
- DAVINCI_MCBSP_WORD_32,
-};
-
-static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
- [SNDRV_PCM_FORMAT_S8] = 1,
- [SNDRV_PCM_FORMAT_S16_LE] = 2,
- [SNDRV_PCM_FORMAT_S32_LE] = 4,
-};
-
-static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
- [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
- [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
- [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
-};
-
-static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
- [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE,
- [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE,
-};
-
-struct davinci_mcbsp_dev {
- struct device *dev;
- struct snd_dmaengine_dai_dma_data dma_data[2];
- int dma_request[2];
- void __iomem *base;
-#define MOD_DSP_A 0
-#define MOD_DSP_B 1
- int mode;
- u32 pcr;
- struct clk *clk;
- /*
- * Combining both channels into 1 element will at least double the
- * amount of time between servicing the dma channel, increase
- * effiency, and reduce the chance of overrun/underrun. But,
- * it will result in the left & right channels being swapped.
- *
- * If relabeling the left and right channels is not possible,
- * you may want to let the codec know to swap them back.
- *
- * It may allow x10 the amount of time to service dma requests,
- * if the codec is master and is using an unnecessarily fast bit clock
- * (ie. tlvaic23b), independent of the sample rate. So, having an
- * entire frame at once means it can be serviced at the sample rate
- * instead of the bit clock rate.
- *
- * In the now unlikely case that an underrun still
- * occurs, both the left and right samples will be repeated
- * so that no pops are heard, and the left and right channels
- * won't end up being swapped because of the underrun.
- */
- unsigned enable_channel_combine:1;
-
- unsigned int fmt;
- int clk_div;
- int clk_input_pin;
- bool i2s_accurate_sck;
-};
-
-static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
- int reg, u32 val)
-{
- __raw_writel(val, dev->base + reg);
-}
-
-static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
-{
- return __raw_readl(dev->base + reg);
-}
-
-static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
-{
- u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP;
- /* The clock needs to toggle to complete reset.
- * So, fake it by toggling the clk polarity.
- */
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
-}
-
-static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
- struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
- int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- u32 spcr;
- u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST;
- spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- if (spcr & mask) {
- /* start off disabled */
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
- spcr & ~mask);
- toggle_clock(dev, playback);
- }
- if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
- DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) {
- /* Start the sample generator */
- spcr |= DAVINCI_MCBSP_SPCR_GRST;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
- }
-
- if (playback) {
- /* Stop the DMA to avoid data loss */
- /* while the transmitter is out of reset to handle XSYNCERR */
- if (component->driver->ops->trigger) {
- int ret = component->driver->ops->trigger(substream,
- SNDRV_PCM_TRIGGER_STOP);
- if (ret < 0)
- printk(KERN_DEBUG "Playback DMA stop failed\n");
- }
-
- /* Enable the transmitter */
- spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- spcr |= DAVINCI_MCBSP_SPCR_XRST;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
-
- /* wait for any unexpected frame sync error to occur */
- udelay(100);
-
- /* Disable the transmitter to clear any outstanding XSYNCERR */
- spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
- toggle_clock(dev, playback);
-
- /* Restart the DMA */
- if (component->driver->ops->trigger) {
- int ret = component->driver->ops->trigger(substream,
- SNDRV_PCM_TRIGGER_START);
- if (ret < 0)
- printk(KERN_DEBUG "Playback DMA start failed\n");
- }
- }
-
- /* Enable transmitter or receiver */
- spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- spcr |= mask;
-
- if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) {
- /* Start frame sync */
- spcr |= DAVINCI_MCBSP_SPCR_FRST;
- }
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
-}
-
-static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
-{
- u32 spcr;
-
- /* Reset transmitter/receiver and sample rate/frame sync generators */
- spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
- spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
- toggle_clock(dev, playback);
-}
-
-#define DEFAULT_BITPERSAMPLE 16
-
-static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
- unsigned int pcr;
- unsigned int srgr;
- bool inv_fs = false;
- /* Attention srgr is updated by hw_params! */
- srgr = DAVINCI_MCBSP_SRGR_FSGM |
- DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
- DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
-
- dev->fmt = fmt;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* cpu is master */
- pcr = DAVINCI_MCBSP_PCR_FSXM |
- DAVINCI_MCBSP_PCR_FSRM |
- DAVINCI_MCBSP_PCR_CLKXM |
- DAVINCI_MCBSP_PCR_CLKRM;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
- /*
- * Selection of the clock input pin that is the
- * input for the Sample Rate Generator.
- * McBSP FSR and FSX are driven by the Sample Rate
- * Generator.
- */
- switch (dev->clk_input_pin) {
- case MCBSP_CLKS:
- pcr |= DAVINCI_MCBSP_PCR_CLKXM |
- DAVINCI_MCBSP_PCR_CLKRM;
- break;
- case MCBSP_CLKR:
- pcr |= DAVINCI_MCBSP_PCR_SCLKME;
- break;
- default:
- dev_err(dev->dev, "bad clk_input_pin\n");
- return -EINVAL;
- }
-
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* codec is master */
- pcr = 0;
- break;
- default:
- printk(KERN_ERR "%s:bad master\n", __func__);
- return -EINVAL;
- }
-
- /* interface format */
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- /* Davinci doesn't support TRUE I2S, but some codecs will have
- * the left and right channels contiguous. This allows
- * dsp_a mode to be used with an inverted normal frame clk.
- * If your codec is master and does not have contiguous
- * channels, then you will have sound on only one channel.
- * Try using a different mode, or codec as slave.
- *
- * The TLV320AIC33 is an example of a codec where this works.
- * It has a variable bit clock frequency allowing it to have
- * valid data on every bit clock.
- *
- * The TLV320AIC23 is an example of a codec where this does not
- * work. It has a fixed bit clock frequency with progressively
- * more empty bit clock slots between channels as the sample
- * rate is lowered.
- */
- inv_fs = true;
- /* fall through */
- case SND_SOC_DAIFMT_DSP_A:
- dev->mode = MOD_DSP_A;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- dev->mode = MOD_DSP_B;
- break;
- default:
- printk(KERN_ERR "%s:bad format\n", __func__);
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- /* CLKRP Receive clock polarity,
- * 1 - sampled on rising edge of CLKR
- * valid on rising edge
- * CLKXP Transmit clock polarity,
- * 1 - clocked on falling edge of CLKX
- * valid on rising edge
- * FSRP Receive frame sync pol, 0 - active high
- * FSXP Transmit frame sync pol, 0 - active high
- */
- pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
- break;
- case SND_SOC_DAIFMT_IB_IF:
- /* CLKRP Receive clock polarity,
- * 0 - sampled on falling edge of CLKR
- * valid on falling edge
- * CLKXP Transmit clock polarity,
- * 0 - clocked on rising edge of CLKX
- * valid on falling edge
- * FSRP Receive frame sync pol, 1 - active low
- * FSXP Transmit frame sync pol, 1 - active low
- */
- pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
- break;
- case SND_SOC_DAIFMT_NB_IF:
- /* CLKRP Receive clock polarity,
- * 1 - sampled on rising edge of CLKR
- * valid on rising edge
- * CLKXP Transmit clock polarity,
- * 1 - clocked on falling edge of CLKX
- * valid on rising edge
- * FSRP Receive frame sync pol, 1 - active low
- * FSXP Transmit frame sync pol, 1 - active low
- */
- pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
- DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
- break;
- case SND_SOC_DAIFMT_IB_NF:
- /* CLKRP Receive clock polarity,
- * 0 - sampled on falling edge of CLKR
- * valid on falling edge
- * CLKXP Transmit clock polarity,
- * 0 - clocked on rising edge of CLKX
- * valid on falling edge
- * FSRP Receive frame sync pol, 0 - active high
- * FSXP Transmit frame sync pol, 0 - active high
- */
- break;
- default:
- return -EINVAL;
- }
- if (inv_fs == true)
- pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
- dev->pcr = pcr;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
- return 0;
-}
-
-static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
-
- if (div_id != DAVINCI_MCBSP_CLKGDV)
- return -ENODEV;
-
- dev->clk_div = div;
- return 0;
-}
-
-static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
- struct snd_interval *i = NULL;
- int mcbsp_word_length, master;
- unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
- u32 spcr;
- snd_pcm_format_t fmt;
- unsigned element_cnt = 1;
-
- /* general line settings */
- spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
- } else {
- spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
- }
-
- master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
- fmt = params_format(params);
- mcbsp_word_length = asp_word_length[fmt];
-
- switch (master) {
- case SND_SOC_DAIFMT_CBS_CFS:
- freq = clk_get_rate(dev->clk);
- srgr = DAVINCI_MCBSP_SRGR_FSGM |
- DAVINCI_MCBSP_SRGR_CLKSM;
- srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
- 8 - 1);
- if (dev->i2s_accurate_sck) {
- clk_div = 256;
- do {
- framesize = (freq / (--clk_div)) /
- params->rate_num *
- params->rate_den;
- } while (((framesize < 33) || (framesize > 4095)) &&
- (clk_div));
- clk_div--;
- srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
- } else {
- /* symmetric waveforms */
- clk_div = freq / (mcbsp_word_length * 16) /
- params->rate_num * params->rate_den;
- srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
- 16 - 1);
- }
- clk_div &= 0xFF;
- srgr |= clk_div;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- srgr = DAVINCI_MCBSP_SRGR_FSGM;
- clk_div = dev->clk_div - 1;
- srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
- srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
- clk_div &= 0xFF;
- srgr |= clk_div;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Clock and frame sync given from external sources */
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
- srgr = DAVINCI_MCBSP_SRGR_FSGM;
- srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
- pr_debug("%s - %d FWID set: re-read srgr = %X\n",
- __func__, __LINE__, snd_interval_value(i) - 1);
-
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
- srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
- break;
- default:
- return -EINVAL;
- }
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
-
- rcr = DAVINCI_MCBSP_RCR_RFIG;
- xcr = DAVINCI_MCBSP_XCR_XFIG;
- if (dev->mode == MOD_DSP_B) {
- rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
- xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
- } else {
- rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
- xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
- }
- /* Determine xfer data type */
- fmt = params_format(params);
- if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
- printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
- return -EINVAL;
- }
-
- if (params_channels(params) == 2) {
- element_cnt = 2;
- if (double_fmt[fmt] && dev->enable_channel_combine) {
- element_cnt = 1;
- fmt = double_fmt[fmt];
- }
- switch (master) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
- rcr |= DAVINCI_MCBSP_RCR_RPHASE;
- xcr |= DAVINCI_MCBSP_XCR_XPHASE;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
- break;
- default:
- return -EINVAL;
- }
- }
- mcbsp_word_length = asp_word_length[fmt];
-
- switch (master) {
- case SND_SOC_DAIFMT_CBS_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
- break;
- default:
- return -EINVAL;
- }
-
- rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
- xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
- DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
- else
- davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
-
- pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr);
- pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr);
- pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr);
- return 0;
-}
-
-static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
- int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- davinci_mcbsp_stop(dev, playback);
- return 0;
-}
-
-static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
- int ret = 0;
- int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- davinci_mcbsp_start(dev, substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_mcbsp_stop(dev, playback);
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
-}
-
-static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
- int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- davinci_mcbsp_stop(dev, playback);
-}
-
-#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
-
-static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
- .shutdown = davinci_i2s_shutdown,
- .prepare = davinci_i2s_prepare,
- .trigger = davinci_i2s_trigger,
- .hw_params = davinci_i2s_hw_params,
- .set_fmt = davinci_i2s_set_dai_fmt,
- .set_clkdiv = davinci_i2s_dai_set_clkdiv,
-
-};
-
-static int davinci_i2s_dai_probe(struct snd_soc_dai *dai)
-{
- struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
-
- return 0;
-}
-
-static struct snd_soc_dai_driver davinci_i2s_dai = {
- .probe = davinci_i2s_dai_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = DAVINCI_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .channels_min = 2,
- .channels_max = 2,
- .rates = DAVINCI_I2S_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &davinci_i2s_dai_ops,
-
-};
-
-static const struct snd_soc_component_driver davinci_i2s_component = {
- .name = DRV_NAME,
-};
-
-static int davinci_i2s_probe(struct platform_device *pdev)
-{
- struct snd_dmaengine_dai_dma_data *dma_data;
- struct davinci_mcbsp_dev *dev;
- struct resource *mem, *res;
- void __iomem *io_base;
- int *dma;
- int ret;
-
- mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (!mem) {
- dev_warn(&pdev->dev,
- "\"mpu\" mem resource not found, using index 0\n");
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -ENODEV;
- }
- }
-
- io_base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(io_base))
- return PTR_ERR(io_base);
-
- dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev),
- GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- dev->base = io_base;
-
- /* setup DMA, first TX, then RX */
- dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (res) {
- dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
- *dma = res->start;
- dma_data->filter_data = dma;
- } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
- dma_data->filter_data = "tx";
- } else {
- dev_err(&pdev->dev, "Missing DMA tx resource\n");
- return -ENODEV;
- }
-
- dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
- dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (res) {
- dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
- *dma = res->start;
- dma_data->filter_data = dma;
- } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
- dma_data->filter_data = "rx";
- } else {
- dev_err(&pdev->dev, "Missing DMA rx resource\n");
- return -ENODEV;
- }
-
- dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk))
- return -ENODEV;
- clk_enable(dev->clk);
-
- dev->dev = &pdev->dev;
- dev_set_drvdata(&pdev->dev, dev);
-
- ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component,
- &davinci_i2s_dai, 1);
- if (ret != 0)
- goto err_release_clk;
-
- ret = edma_pcm_platform_register(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
- goto err_unregister_component;
- }
-
- return 0;
-
-err_unregister_component:
- snd_soc_unregister_component(&pdev->dev);
-err_release_clk:
- clk_disable(dev->clk);
- clk_put(dev->clk);
- return ret;
-}
-
-static int davinci_i2s_remove(struct platform_device *pdev)
-{
- struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev);
-
- snd_soc_unregister_component(&pdev->dev);
-
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
-
- return 0;
-}
-
-static const struct of_device_id davinci_i2s_match[] = {
- { .compatible = "ti,da850-mcbsp" },
- {},
-};
-MODULE_DEVICE_TABLE(of, davinci_i2s_match);
-
-static struct platform_driver davinci_mcbsp_driver = {
- .probe = davinci_i2s_probe,
- .remove = davinci_i2s_remove,
- .driver = {
- .name = "davinci-mcbsp",
- .of_match_table = of_match_ptr(davinci_i2s_match),
- },
-};
-
-module_platform_driver(davinci_mcbsp_driver);
-
-MODULE_AUTHOR("Vladimir Barinov");
-MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
- *
- * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
- * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
- *
- * 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 _DAVINCI_I2S_H
-#define _DAVINCI_I2S_H
-
-/* McBSP dividers */
-enum davinci_mcbsp_div {
- DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */
-};
-
-#endif
+++ /dev/null
-/*
- * ALSA SoC McASP Audio Layer for TI DAVINCI processor
- *
- * Multi-channel Audio Serial Port Driver
- *
- * Author: Nirmal Pandey <n-pandey@ti.com>,
- * Suresh Rajashekara <suresh.r@ti.com>
- * Steve Chen <schen@.mvista.com>
- *
- * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
- * Copyright: (C) 2009 Texas Instruments, India
- *
- * 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.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_device.h>
-#include <linux/platform_data/davinci_asp.h>
-#include <linux/math64.h>
-#include <linux/bitmap.h>
-
-#include <sound/asoundef.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "edma-pcm.h"
-#include "../omap/sdma-pcm.h"
-#include "davinci-mcasp.h"
-
-#define MCASP_MAX_AFIFO_DEPTH 64
-
-static u32 context_regs[] = {
- DAVINCI_MCASP_TXFMCTL_REG,
- DAVINCI_MCASP_RXFMCTL_REG,
- DAVINCI_MCASP_TXFMT_REG,
- DAVINCI_MCASP_RXFMT_REG,
- DAVINCI_MCASP_ACLKXCTL_REG,
- DAVINCI_MCASP_ACLKRCTL_REG,
- DAVINCI_MCASP_AHCLKXCTL_REG,
- DAVINCI_MCASP_AHCLKRCTL_REG,
- DAVINCI_MCASP_PDIR_REG,
- DAVINCI_MCASP_RXMASK_REG,
- DAVINCI_MCASP_TXMASK_REG,
- DAVINCI_MCASP_RXTDM_REG,
- DAVINCI_MCASP_TXTDM_REG,
-};
-
-struct davinci_mcasp_context {
- u32 config_regs[ARRAY_SIZE(context_regs)];
- u32 afifo_regs[2]; /* for read/write fifo control registers */
- u32 *xrsr_regs; /* for serializer configuration */
- bool pm_state;
-};
-
-struct davinci_mcasp_ruledata {
- struct davinci_mcasp *mcasp;
- int serializers;
-};
-
-struct davinci_mcasp {
- struct snd_dmaengine_dai_dma_data dma_data[2];
- void __iomem *base;
- u32 fifo_base;
- struct device *dev;
- struct snd_pcm_substream *substreams[2];
- unsigned int dai_fmt;
-
- /* McASP specific data */
- int tdm_slots;
- u32 tdm_mask[2];
- int slot_width;
- u8 op_mode;
- u8 dismod;
- u8 num_serializer;
- u8 *serial_dir;
- u8 version;
- u8 bclk_div;
- int streams;
- u32 irq_request[2];
- int dma_request[2];
-
- int sysclk_freq;
- bool bclk_master;
-
- unsigned long pdir; /* Pin direction bitfield */
-
- /* McASP FIFO related */
- u8 txnumevt;
- u8 rxnumevt;
-
- bool dat_port;
-
- /* Used for comstraint setting on the second stream */
- u32 channels;
-
-#ifdef CONFIG_PM_SLEEP
- struct davinci_mcasp_context context;
-#endif
-
- struct davinci_mcasp_ruledata ruledata[2];
- struct snd_pcm_hw_constraint_list chconstr[2];
-};
-
-static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
- u32 val)
-{
- void __iomem *reg = mcasp->base + offset;
- __raw_writel(__raw_readl(reg) | val, reg);
-}
-
-static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset,
- u32 val)
-{
- void __iomem *reg = mcasp->base + offset;
- __raw_writel((__raw_readl(reg) & ~(val)), reg);
-}
-
-static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset,
- u32 val, u32 mask)
-{
- void __iomem *reg = mcasp->base + offset;
- __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
-}
-
-static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset,
- u32 val)
-{
- __raw_writel(val, mcasp->base + offset);
-}
-
-static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset)
-{
- return (u32)__raw_readl(mcasp->base + offset);
-}
-
-static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val)
-{
- int i = 0;
-
- mcasp_set_bits(mcasp, ctl_reg, val);
-
- /* programming GBLCTL needs to read back from GBLCTL and verfiy */
- /* loop count is to avoid the lock-up */
- for (i = 0; i < 1000; i++) {
- if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val)
- break;
- }
-
- if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val))
- printk(KERN_ERR "GBLCTL write error\n");
-}
-
-static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
-{
- u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
- u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
-
- return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
-}
-
-static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable)
-{
- u32 bit = PIN_BIT_AMUTE;
-
- for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) {
- if (enable)
- mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
- else
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
- }
-}
-
-static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
-{
- u32 bit;
-
- for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AFSR) {
- if (enable)
- mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
- else
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
- }
-}
-
-static void mcasp_start_rx(struct davinci_mcasp *mcasp)
-{
- if (mcasp->rxnumevt) { /* enable FIFO */
- u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
- }
-
- /* Start clocks */
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
- /*
- * When ASYNC == 0 the transmit and receive sections operate
- * synchronously from the transmit clock and frame sync. We need to make
- * sure that the TX signlas are enabled when starting reception.
- */
- if (mcasp_is_synchronous(mcasp)) {
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- }
-
- /* Activate serializer(s) */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
- /* Release RX state machine */
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
- /* Release Frame Sync generator */
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
- if (mcasp_is_synchronous(mcasp))
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
-
- /* enable receive IRQs */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
- mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
-}
-
-static void mcasp_start_tx(struct davinci_mcasp *mcasp)
-{
- u32 cnt;
-
- if (mcasp->txnumevt) { /* enable FIFO */
- u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
- }
-
- /* Start clocks */
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_clk_pdir(mcasp, true);
-
- /* Activate serializer(s) */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
-
- /* wait for XDATA to be cleared */
- cnt = 0;
- while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) &&
- (cnt < 100000))
- cnt++;
-
- mcasp_set_axr_pdir(mcasp, true);
-
- /* Release TX state machine */
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
- /* Release Frame Sync generator */
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
-
- /* enable transmit IRQs */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
- mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
-}
-
-static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
-{
- mcasp->streams++;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- mcasp_start_tx(mcasp);
- else
- mcasp_start_rx(mcasp);
-}
-
-static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
-{
- /* disable IRQ sources */
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
- mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
-
- /*
- * In synchronous mode stop the TX clocks if no other stream is
- * running
- */
- if (mcasp_is_synchronous(mcasp) && !mcasp->streams) {
- mcasp_set_clk_pdir(mcasp, false);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
- }
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
-
- if (mcasp->rxnumevt) { /* disable FIFO */
- u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- }
-}
-
-static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
-{
- u32 val = 0;
-
- /* disable IRQ sources */
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
- mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
-
- /*
- * In synchronous mode keep TX clocks running if the capture stream is
- * still running.
- */
- if (mcasp_is_synchronous(mcasp) && mcasp->streams)
- val = TXHCLKRST | TXCLKRST | TXFSRST;
- else
- mcasp_set_clk_pdir(mcasp, false);
-
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
-
- if (mcasp->txnumevt) { /* disable FIFO */
- u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- }
-
- mcasp_set_axr_pdir(mcasp, false);
-}
-
-static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
-{
- mcasp->streams--;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- mcasp_stop_tx(mcasp);
- else
- mcasp_stop_rx(mcasp);
-}
-
-static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
-{
- struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
- struct snd_pcm_substream *substream;
- u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK];
- u32 handled_mask = 0;
- u32 stat;
-
- stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
- if (stat & XUNDRN & irq_mask) {
- dev_warn(mcasp->dev, "Transmit buffer underflow\n");
- handled_mask |= XUNDRN;
-
- substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
- if (substream)
- snd_pcm_stop_xrun(substream);
- }
-
- if (!handled_mask)
- dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n",
- stat);
-
- if (stat & XRERR)
- handled_mask |= XRERR;
-
- /* Ack the handled event only */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask);
-
- return IRQ_RETVAL(handled_mask);
-}
-
-static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
-{
- struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
- struct snd_pcm_substream *substream;
- u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE];
- u32 handled_mask = 0;
- u32 stat;
-
- stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
- if (stat & ROVRN & irq_mask) {
- dev_warn(mcasp->dev, "Receive buffer overflow\n");
- handled_mask |= ROVRN;
-
- substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
- if (substream)
- snd_pcm_stop_xrun(substream);
- }
-
- if (!handled_mask)
- dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n",
- stat);
-
- if (stat & XRERR)
- handled_mask |= XRERR;
-
- /* Ack the handled event only */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask);
-
- return IRQ_RETVAL(handled_mask);
-}
-
-static irqreturn_t davinci_mcasp_common_irq_handler(int irq, void *data)
-{
- struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
- irqreturn_t ret = IRQ_NONE;
-
- if (mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK])
- ret = davinci_mcasp_tx_irq_handler(irq, data);
-
- if (mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE])
- ret |= davinci_mcasp_rx_irq_handler(irq, data);
-
- return ret;
-}
-
-static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
- int ret = 0;
- u32 data_delay;
- bool fs_pol_rising;
- bool inv_fs = false;
-
- if (!fmt)
- return 0;
-
- pm_runtime_get_sync(mcasp->dev);
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_DSP_A:
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
- /* 1st data bit occur one ACLK cycle after the frame sync */
- data_delay = 1;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- case SND_SOC_DAIFMT_AC97:
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
- /* No delay after FS */
- data_delay = 0;
- break;
- case SND_SOC_DAIFMT_I2S:
- /* configure a full-word SYNC pulse (LRCLK) */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
- /* 1st data bit occur one ACLK cycle after the frame sync */
- data_delay = 1;
- /* FS need to be inverted */
- inv_fs = true;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- /* configure a full-word SYNC pulse (LRCLK) */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
- /* No delay after FS */
- data_delay = 0;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay),
- FSXDLY(3));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay),
- FSRDLY(3));
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* codec is clock and frame slave */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
-
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
-
- /* BCLK */
- set_bit(PIN_BIT_ACLKX, &mcasp->pdir);
- set_bit(PIN_BIT_ACLKR, &mcasp->pdir);
- /* Frame Sync */
- set_bit(PIN_BIT_AFSX, &mcasp->pdir);
- set_bit(PIN_BIT_AFSR, &mcasp->pdir);
-
- mcasp->bclk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- /* codec is clock slave and frame master */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
-
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
-
- /* BCLK */
- set_bit(PIN_BIT_ACLKX, &mcasp->pdir);
- set_bit(PIN_BIT_ACLKR, &mcasp->pdir);
- /* Frame Sync */
- clear_bit(PIN_BIT_AFSX, &mcasp->pdir);
- clear_bit(PIN_BIT_AFSR, &mcasp->pdir);
-
- mcasp->bclk_master = 1;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- /* codec is clock master and frame slave */
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
-
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
-
- /* BCLK */
- clear_bit(PIN_BIT_ACLKX, &mcasp->pdir);
- clear_bit(PIN_BIT_ACLKR, &mcasp->pdir);
- /* Frame Sync */
- set_bit(PIN_BIT_AFSX, &mcasp->pdir);
- set_bit(PIN_BIT_AFSR, &mcasp->pdir);
-
- mcasp->bclk_master = 0;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* codec is clock and frame master */
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
-
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
-
- /* BCLK */
- clear_bit(PIN_BIT_ACLKX, &mcasp->pdir);
- clear_bit(PIN_BIT_ACLKR, &mcasp->pdir);
- /* Frame Sync */
- clear_bit(PIN_BIT_AFSX, &mcasp->pdir);
- clear_bit(PIN_BIT_AFSR, &mcasp->pdir);
-
- mcasp->bclk_master = 0;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_IB_NF:
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- fs_pol_rising = true;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- fs_pol_rising = false;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- fs_pol_rising = false;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
- fs_pol_rising = true;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
-
- if (inv_fs)
- fs_pol_rising = !fs_pol_rising;
-
- if (fs_pol_rising) {
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
- } else {
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
- }
-
- mcasp->dai_fmt = fmt;
-out:
- pm_runtime_put(mcasp->dev);
- return ret;
-}
-
-static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
- int div, bool explicit)
-{
- pm_runtime_get_sync(mcasp->dev);
- switch (div_id) {
- case MCASP_CLKDIV_AUXCLK: /* MCLK divider */
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
- AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
- AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
- break;
-
- case MCASP_CLKDIV_BCLK: /* BCLK divider */
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
- ACLKXDIV(div - 1), ACLKXDIV_MASK);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
- ACLKRDIV(div - 1), ACLKRDIV_MASK);
- if (explicit)
- mcasp->bclk_div = div;
- break;
-
- case MCASP_CLKDIV_BCLK_FS_RATIO:
- /*
- * BCLK/LRCLK ratio descries how many bit-clock cycles
- * fit into one frame. The clock ratio is given for a
- * full period of data (for I2S format both left and
- * right channels), so it has to be divided by number
- * of tdm-slots (for I2S - divided by 2).
- * Instead of storing this ratio, we calculate a new
- * tdm_slot width by dividing the the ratio by the
- * number of configured tdm slots.
- */
- mcasp->slot_width = div / mcasp->tdm_slots;
- if (div % mcasp->tdm_slots)
- dev_warn(mcasp->dev,
- "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
- __func__, div, mcasp->tdm_slots);
- break;
-
- default:
- return -EINVAL;
- }
-
- pm_runtime_put(mcasp->dev);
- return 0;
-}
-
-static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
- int div)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-
- return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1);
-}
-
-static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_get_sync(mcasp->dev);
- if (dir == SND_SOC_CLOCK_OUT) {
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
- } else {
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
- }
-
- mcasp->sysclk_freq = freq;
-
- pm_runtime_put(mcasp->dev);
- return 0;
-}
-
-/* All serializers must have equal number of channels */
-static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
- int serializers)
-{
- struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
- unsigned int *list = (unsigned int *) cl->list;
- int slots = mcasp->tdm_slots;
- int i, count = 0;
-
- if (mcasp->tdm_mask[stream])
- slots = hweight32(mcasp->tdm_mask[stream]);
-
- for (i = 1; i <= slots; i++)
- list[count++] = i;
-
- for (i = 2; i <= serializers; i++)
- list[count++] = i*slots;
-
- cl->count = count;
-
- return 0;
-}
-
-static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
-{
- int rx_serializers = 0, tx_serializers = 0, ret, i;
-
- for (i = 0; i < mcasp->num_serializer; i++)
- if (mcasp->serial_dir[i] == TX_MODE)
- tx_serializers++;
- else if (mcasp->serial_dir[i] == RX_MODE)
- rx_serializers++;
-
- ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
- tx_serializers);
- if (ret)
- return ret;
-
- ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
- rx_serializers);
-
- return ret;
-}
-
-
-static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask,
- unsigned int rx_mask,
- int slots, int slot_width)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-
- dev_dbg(mcasp->dev,
- "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
- __func__, tx_mask, rx_mask, slots, slot_width);
-
- if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
- dev_err(mcasp->dev,
- "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
- tx_mask, rx_mask, slots);
- return -EINVAL;
- }
-
- if (slot_width &&
- (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
- dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
- __func__, slot_width);
- return -EINVAL;
- }
-
- mcasp->tdm_slots = slots;
- mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
- mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
- mcasp->slot_width = slot_width;
-
- return davinci_mcasp_set_ch_constraints(mcasp);
-}
-
-static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int sample_width)
-{
- u32 fmt;
- u32 tx_rotate = (sample_width / 4) & 0x7;
- u32 mask = (1ULL << sample_width) - 1;
- u32 slot_width = sample_width;
-
- /*
- * For captured data we should not rotate, inversion and masking is
- * enoguh to get the data to the right position:
- * Format data from bus after reverse (XRBUF)
- * S16_LE: |LSB|MSB|xxx|xxx| |xxx|xxx|MSB|LSB|
- * S24_3LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB|
- * S24_LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB|
- * S32_LE: |LSB|DAT|DAT|MSB| |MSB|DAT|DAT|LSB|
- */
- u32 rx_rotate = 0;
-
- /*
- * Setting the tdm slot width either with set_clkdiv() or
- * set_tdm_slot() allows us to for example send 32 bits per
- * channel to the codec, while only 16 of them carry audio
- * payload.
- */
- if (mcasp->slot_width) {
- /*
- * When we have more bclk then it is needed for the
- * data, we need to use the rotation to move the
- * received samples to have correct alignment.
- */
- slot_width = mcasp->slot_width;
- rx_rotate = (slot_width - sample_width) / 4;
- }
-
- /* mapping of the XSSZ bit-field as described in the datasheet */
- fmt = (slot_width >> 1) - 1;
-
- if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
- RXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
- TXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
- TXROT(7));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
- RXROT(7));
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
- }
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
-
- return 0;
-}
-
-static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
- int period_words, int channels)
-{
- struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream];
- int i;
- u8 tx_ser = 0;
- u8 rx_ser = 0;
- u8 slots = mcasp->tdm_slots;
- u8 max_active_serializers = (channels + slots - 1) / slots;
- int active_serializers, numevt;
- u32 reg;
- /* Default configuration */
- if (mcasp->version < MCASP_VERSION_3)
- mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
-
- /* All PINS as McASP */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
- } else {
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS);
- }
-
- for (i = 0; i < mcasp->num_serializer; i++) {
- mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
- mcasp->serial_dir[i]);
- if (mcasp->serial_dir[i] == TX_MODE &&
- tx_ser < max_active_serializers) {
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
- mcasp->dismod, DISMOD_MASK);
- set_bit(PIN_BIT_AXR(i), &mcasp->pdir);
- tx_ser++;
- } else if (mcasp->serial_dir[i] == RX_MODE &&
- rx_ser < max_active_serializers) {
- clear_bit(PIN_BIT_AXR(i), &mcasp->pdir);
- rx_ser++;
- } else if (mcasp->serial_dir[i] == INACTIVE_MODE) {
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
- SRMOD_INACTIVE, SRMOD_MASK);
- clear_bit(PIN_BIT_AXR(i), &mcasp->pdir);
- } else if (mcasp->serial_dir[i] == TX_MODE) {
- /* Unused TX pins, clear PDIR */
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
- mcasp->dismod, DISMOD_MASK);
- clear_bit(PIN_BIT_AXR(i), &mcasp->pdir);
- }
- }
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- active_serializers = tx_ser;
- numevt = mcasp->txnumevt;
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- } else {
- active_serializers = rx_ser;
- numevt = mcasp->rxnumevt;
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- }
-
- if (active_serializers < max_active_serializers) {
- dev_warn(mcasp->dev, "stream has more channels (%d) than are "
- "enabled in mcasp (%d)\n", channels,
- active_serializers * slots);
- return -EINVAL;
- }
-
- /* AFIFO is not in use */
- if (!numevt) {
- /* Configure the burst size for platform drivers */
- if (active_serializers > 1) {
- /*
- * If more than one serializers are in use we have one
- * DMA request to provide data for all serializers.
- * For example if three serializers are enabled the DMA
- * need to transfer three words per DMA request.
- */
- dma_data->maxburst = active_serializers;
- } else {
- dma_data->maxburst = 0;
- }
- return 0;
- }
-
- if (period_words % active_serializers) {
- dev_err(mcasp->dev, "Invalid combination of period words and "
- "active serializers: %d, %d\n", period_words,
- active_serializers);
- return -EINVAL;
- }
-
- /*
- * Calculate the optimal AFIFO depth for platform side:
- * The number of words for numevt need to be in steps of active
- * serializers.
- */
- numevt = (numevt / active_serializers) * active_serializers;
-
- while (period_words % numevt && numevt > 0)
- numevt -= active_serializers;
- if (numevt <= 0)
- numevt = active_serializers;
-
- mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK);
- mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK);
-
- /* Configure the burst size for platform drivers */
- if (numevt == 1)
- numevt = 0;
- dma_data->maxburst = numevt;
-
- return 0;
-}
-
-static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
- int channels)
-{
- int i, active_slots;
- int total_slots;
- int active_serializers;
- u32 mask = 0;
- u32 busel = 0;
-
- total_slots = mcasp->tdm_slots;
-
- /*
- * If more than one serializer is needed, then use them with
- * all the specified tdm_slots. Otherwise, one serializer can
- * cope with the transaction using just as many slots as there
- * are channels in the stream.
- */
- if (mcasp->tdm_mask[stream]) {
- active_slots = hweight32(mcasp->tdm_mask[stream]);
- active_serializers = (channels + active_slots - 1) /
- active_slots;
- if (active_serializers == 1) {
- active_slots = channels;
- for (i = 0; i < total_slots; i++) {
- if ((1 << i) & mcasp->tdm_mask[stream]) {
- mask |= (1 << i);
- if (--active_slots <= 0)
- break;
- }
- }
- }
- } else {
- active_serializers = (channels + total_slots - 1) / total_slots;
- if (active_serializers == 1)
- active_slots = channels;
- else
- active_slots = total_slots;
-
- for (i = 0; i < active_slots; i++)
- mask |= (1 << i);
- }
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
-
- if (!mcasp->dat_port)
- busel = TXSEL;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(total_slots), FSXMOD(0x1FF));
- } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(total_slots), FSRMOD(0x1FF));
- /*
- * If McASP is set to be TX/RX synchronous and the playback is
- * not running already we need to configure the TX slots in
- * order to have correct FSX on the bus
- */
- if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(total_slots), FSXMOD(0x1FF));
- }
-
- return 0;
-}
-
-/* S/PDIF */
-static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
- unsigned int rate)
-{
- u32 cs_value = 0;
- u8 *cs_bytes = (u8*) &cs_value;
-
- /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
- and LSB first */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15));
-
- /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180));
-
- /* Set the TX tdm : for all the slots */
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
-
- /* Set the TX clock controls : div = 1 and internal */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC);
-
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
-
- /* Only 44100 and 48000 are valid, both have the same setting */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
-
- /* Enable the DIT */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN);
-
- /* Set S/PDIF channel status bits */
- cs_bytes[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
- cs_bytes[1] = IEC958_AES1_CON_PCM_CODER;
-
- switch (rate) {
- case 22050:
- cs_bytes[3] |= IEC958_AES3_CON_FS_22050;
- break;
- case 24000:
- cs_bytes[3] |= IEC958_AES3_CON_FS_24000;
- break;
- case 32000:
- cs_bytes[3] |= IEC958_AES3_CON_FS_32000;
- break;
- case 44100:
- cs_bytes[3] |= IEC958_AES3_CON_FS_44100;
- break;
- case 48000:
- cs_bytes[3] |= IEC958_AES3_CON_FS_48000;
- break;
- case 88200:
- cs_bytes[3] |= IEC958_AES3_CON_FS_88200;
- break;
- case 96000:
- cs_bytes[3] |= IEC958_AES3_CON_FS_96000;
- break;
- case 176400:
- cs_bytes[3] |= IEC958_AES3_CON_FS_176400;
- break;
- case 192000:
- cs_bytes[3] |= IEC958_AES3_CON_FS_192000;
- break;
- default:
- printk(KERN_WARNING "unsupported sampling rate: %d\n", rate);
- return -EINVAL;
- }
-
- mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRA_REG, cs_value);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRB_REG, cs_value);
-
- return 0;
-}
-
-static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
- unsigned int bclk_freq, bool set)
-{
- int error_ppm;
- unsigned int sysclk_freq = mcasp->sysclk_freq;
- u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
- int div = sysclk_freq / bclk_freq;
- int rem = sysclk_freq % bclk_freq;
- int aux_div = 1;
-
- if (div > (ACLKXDIV_MASK + 1)) {
- if (reg & AHCLKXE) {
- aux_div = div / (ACLKXDIV_MASK + 1);
- if (div % (ACLKXDIV_MASK + 1))
- aux_div++;
-
- sysclk_freq /= aux_div;
- div = sysclk_freq / bclk_freq;
- rem = sysclk_freq % bclk_freq;
- } else if (set) {
- dev_warn(mcasp->dev, "Too fast reference clock (%u)\n",
- sysclk_freq);
- }
- }
-
- if (rem != 0) {
- if (div == 0 ||
- ((sysclk_freq / div) - bclk_freq) >
- (bclk_freq - (sysclk_freq / (div+1)))) {
- div++;
- rem = rem - bclk_freq;
- }
- }
- error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem,
- (int)bclk_freq)) / div - 1000000;
-
- if (set) {
- if (error_ppm)
- dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
- error_ppm);
-
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
- if (reg & AHCLKXE)
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
- aux_div, 0);
- }
-
- return error_ppm;
-}
-
-static inline u32 davinci_mcasp_tx_delay(struct davinci_mcasp *mcasp)
-{
- if (!mcasp->txnumevt)
- return 0;
-
- return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_WFIFOSTS_OFFSET);
-}
-
-static inline u32 davinci_mcasp_rx_delay(struct davinci_mcasp *mcasp)
-{
- if (!mcasp->rxnumevt)
- return 0;
-
- return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_RFIFOSTS_OFFSET);
-}
-
-static snd_pcm_sframes_t davinci_mcasp_delay(
- struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
- u32 fifo_use;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- fifo_use = davinci_mcasp_tx_delay(mcasp);
- else
- fifo_use = davinci_mcasp_rx_delay(mcasp);
-
- /*
- * Divide the used locations with the channel count to get the
- * FIFO usage in samples (don't care about partial samples in the
- * buffer).
- */
- return fifo_use / substream->runtime->channels;
-}
-
-static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
- int word_length;
- int channels = params_channels(params);
- int period_size = params_period_size(params);
- int ret;
-
- ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt);
- if (ret)
- return ret;
-
- /*
- * If mcasp is BCLK master, and a BCLK divider was not provided by
- * the machine driver, we need to calculate the ratio.
- */
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
- int slots = mcasp->tdm_slots;
- int rate = params_rate(params);
- int sbits = params_width(params);
-
- if (mcasp->slot_width)
- sbits = mcasp->slot_width;
-
- davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true);
- }
-
- ret = mcasp_common_hw_param(mcasp, substream->stream,
- period_size * channels, channels);
- if (ret)
- return ret;
-
- if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
- ret = mcasp_dit_hw_param(mcasp, params_rate(params));
- else
- ret = mcasp_i2s_hw_param(mcasp, substream->stream,
- channels);
-
- if (ret)
- return ret;
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_U8:
- case SNDRV_PCM_FORMAT_S8:
- word_length = 8;
- break;
-
- case SNDRV_PCM_FORMAT_U16_LE:
- case SNDRV_PCM_FORMAT_S16_LE:
- word_length = 16;
- break;
-
- case SNDRV_PCM_FORMAT_U24_3LE:
- case SNDRV_PCM_FORMAT_S24_3LE:
- word_length = 24;
- break;
-
- case SNDRV_PCM_FORMAT_U24_LE:
- case SNDRV_PCM_FORMAT_S24_LE:
- word_length = 24;
- break;
-
- case SNDRV_PCM_FORMAT_U32_LE:
- case SNDRV_PCM_FORMAT_S32_LE:
- word_length = 32;
- break;
-
- default:
- printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
- return -EINVAL;
- }
-
- davinci_config_channel_size(mcasp, word_length);
-
- if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
- mcasp->channels = channels;
-
- return 0;
-}
-
-static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *cpu_dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- davinci_mcasp_start(mcasp, substream->stream);
- break;
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_mcasp_stop(mcasp, substream->stream);
- break;
-
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-static const unsigned int davinci_mcasp_dai_rates[] = {
- 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
- 88200, 96000, 176400, 192000,
-};
-
-#define DAVINCI_MAX_RATE_ERROR_PPM 1000
-
-static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct davinci_mcasp_ruledata *rd = rule->private;
- struct snd_interval *ri =
- hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
- int sbits = params_width(params);
- int slots = rd->mcasp->tdm_slots;
- struct snd_interval range;
- int i;
-
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
-
- snd_interval_any(&range);
- range.empty = 1;
-
- for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) {
- if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) {
- uint bclk_freq = sbits*slots*
- davinci_mcasp_dai_rates[i];
- int ppm;
-
- ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq,
- false);
- if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
- if (range.empty) {
- range.min = davinci_mcasp_dai_rates[i];
- range.empty = 0;
- }
- range.max = davinci_mcasp_dai_rates[i];
- }
- }
- }
-
- dev_dbg(rd->mcasp->dev,
- "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n",
- ri->min, ri->max, range.min, range.max, sbits, slots);
-
- return snd_interval_refine(hw_param_interval(params, rule->var),
- &range);
-}
-
-static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct davinci_mcasp_ruledata *rd = rule->private;
- struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
- struct snd_mask nfmt;
- int rate = params_rate(params);
- int slots = rd->mcasp->tdm_slots;
- int i, count = 0;
-
- snd_mask_none(&nfmt);
-
- for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
- if (snd_mask_test(fmt, i)) {
- uint sbits = snd_pcm_format_width(i);
- int ppm;
-
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
-
- ppm = davinci_mcasp_calc_clk_div(rd->mcasp,
- sbits * slots * rate,
- false);
- if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
- snd_mask_set(&nfmt, i);
- count++;
- }
- }
- }
- dev_dbg(rd->mcasp->dev,
- "%d possible sample format for %d Hz and %d tdm slots\n",
- count, rate, slots);
-
- return snd_mask_refine(fmt, &nfmt);
-}
-
-static int davinci_mcasp_hw_rule_min_periodsize(
- struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
-{
- struct snd_interval *period_size = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
- struct snd_interval frames;
-
- snd_interval_any(&frames);
- frames.min = 64;
- frames.integer = 1;
-
- return snd_interval_refine(period_size, &frames);
-}
-
-static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
- struct davinci_mcasp_ruledata *ruledata =
- &mcasp->ruledata[substream->stream];
- u32 max_channels = 0;
- int i, dir;
- int tdm_slots = mcasp->tdm_slots;
-
- /* Do not allow more then one stream per direction */
- if (mcasp->substreams[substream->stream])
- return -EBUSY;
-
- mcasp->substreams[substream->stream] = substream;
-
- if (mcasp->tdm_mask[substream->stream])
- tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
-
- if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
- return 0;
-
- /*
- * Limit the maximum allowed channels for the first stream:
- * number of serializers for the direction * tdm slots per serializer
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dir = TX_MODE;
- else
- dir = RX_MODE;
-
- for (i = 0; i < mcasp->num_serializer; i++) {
- if (mcasp->serial_dir[i] == dir)
- max_channels++;
- }
- ruledata->serializers = max_channels;
- max_channels *= tdm_slots;
- /*
- * If the already active stream has less channels than the calculated
- * limnit based on the seirializers * tdm_slots, we need to use that as
- * a constraint for the second stream.
- * Otherwise (first stream or less allowed channels) we use the
- * calculated constraint.
- */
- if (mcasp->channels && mcasp->channels < max_channels)
- max_channels = mcasp->channels;
- /*
- * But we can always allow channels upto the amount of
- * the available tdm_slots.
- */
- if (max_channels < tdm_slots)
- max_channels = tdm_slots;
-
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- 0, max_channels);
-
- snd_pcm_hw_constraint_list(substream->runtime,
- 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &mcasp->chconstr[substream->stream]);
-
- if (mcasp->slot_width)
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- 8, mcasp->slot_width);
-
- /*
- * If we rely on implicit BCLK divider setting we should
- * set constraints based on what we can provide.
- */
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
- int ret;
-
- ruledata->mcasp = mcasp;
-
- ret = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- davinci_mcasp_hw_rule_rate,
- ruledata,
- SNDRV_PCM_HW_PARAM_FORMAT, -1);
- if (ret)
- return ret;
- ret = snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_FORMAT,
- davinci_mcasp_hw_rule_format,
- ruledata,
- SNDRV_PCM_HW_PARAM_RATE, -1);
- if (ret)
- return ret;
- }
-
- snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
- davinci_mcasp_hw_rule_min_periodsize, NULL,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
-
- return 0;
-}
-
-static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
-
- mcasp->substreams[substream->stream] = NULL;
-
- if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
- return;
-
- if (!cpu_dai->active)
- mcasp->channels = 0;
-}
-
-static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
- .startup = davinci_mcasp_startup,
- .shutdown = davinci_mcasp_shutdown,
- .trigger = davinci_mcasp_trigger,
- .delay = davinci_mcasp_delay,
- .hw_params = davinci_mcasp_hw_params,
- .set_fmt = davinci_mcasp_set_dai_fmt,
- .set_clkdiv = davinci_mcasp_set_clkdiv,
- .set_sysclk = davinci_mcasp_set_sysclk,
- .set_tdm_slot = davinci_mcasp_set_tdm_slot,
-};
-
-static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
-
- dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
- struct davinci_mcasp_context *context = &mcasp->context;
- u32 reg;
- int i;
-
- context->pm_state = pm_runtime_active(mcasp->dev);
- if (!context->pm_state)
- pm_runtime_get_sync(mcasp->dev);
-
- for (i = 0; i < ARRAY_SIZE(context_regs); i++)
- context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
-
- if (mcasp->txnumevt) {
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
- }
- if (mcasp->rxnumevt) {
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
- }
-
- for (i = 0; i < mcasp->num_serializer; i++)
- context->xrsr_regs[i] = mcasp_get_reg(mcasp,
- DAVINCI_MCASP_XRSRCTL_REG(i));
-
- pm_runtime_put_sync(mcasp->dev);
-
- return 0;
-}
-
-static int davinci_mcasp_resume(struct snd_soc_dai *dai)
-{
- struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
- struct davinci_mcasp_context *context = &mcasp->context;
- u32 reg;
- int i;
-
- pm_runtime_get_sync(mcasp->dev);
-
- for (i = 0; i < ARRAY_SIZE(context_regs); i++)
- mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
-
- if (mcasp->txnumevt) {
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
- }
- if (mcasp->rxnumevt) {
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
- }
-
- for (i = 0; i < mcasp->num_serializer; i++)
- mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
- context->xrsr_regs[i]);
-
- if (!context->pm_state)
- pm_runtime_put_sync(mcasp->dev);
-
- return 0;
-}
-#else
-#define davinci_mcasp_suspend NULL
-#define davinci_mcasp_resume NULL
-#endif
-
-#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
-
-#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_U8 | \
- SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_U16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_U24_LE | \
- SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_U24_3LE | \
- SNDRV_PCM_FMTBIT_S32_LE | \
- SNDRV_PCM_FMTBIT_U32_LE)
-
-static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
- {
- .name = "davinci-mcasp.0",
- .probe = davinci_mcasp_dai_probe,
- .suspend = davinci_mcasp_suspend,
- .resume = davinci_mcasp_resume,
- .playback = {
- .channels_min = 1,
- .channels_max = 32 * 16,
- .rates = DAVINCI_MCASP_RATES,
- .formats = DAVINCI_MCASP_PCM_FMTS,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32 * 16,
- .rates = DAVINCI_MCASP_RATES,
- .formats = DAVINCI_MCASP_PCM_FMTS,
- },
- .ops = &davinci_mcasp_dai_ops,
-
- .symmetric_samplebits = 1,
- .symmetric_rates = 1,
- },
- {
- .name = "davinci-mcasp.1",
- .probe = davinci_mcasp_dai_probe,
- .playback = {
- .channels_min = 1,
- .channels_max = 384,
- .rates = DAVINCI_MCASP_RATES,
- .formats = DAVINCI_MCASP_PCM_FMTS,
- },
- .ops = &davinci_mcasp_dai_ops,
- },
-
-};
-
-static const struct snd_soc_component_driver davinci_mcasp_component = {
- .name = "davinci-mcasp",
-};
-
-/* Some HW specific values and defaults. The rest is filled in from DT. */
-static struct davinci_mcasp_pdata dm646x_mcasp_pdata = {
- .tx_dma_offset = 0x400,
- .rx_dma_offset = 0x400,
- .version = MCASP_VERSION_1,
-};
-
-static struct davinci_mcasp_pdata da830_mcasp_pdata = {
- .tx_dma_offset = 0x2000,
- .rx_dma_offset = 0x2000,
- .version = MCASP_VERSION_2,
-};
-
-static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
- .tx_dma_offset = 0,
- .rx_dma_offset = 0,
- .version = MCASP_VERSION_3,
-};
-
-static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
- /* The CFG port offset will be calculated if it is needed */
- .tx_dma_offset = 0,
- .rx_dma_offset = 0,
- .version = MCASP_VERSION_4,
-};
-
-static const struct of_device_id mcasp_dt_ids[] = {
- {
- .compatible = "ti,dm646x-mcasp-audio",
- .data = &dm646x_mcasp_pdata,
- },
- {
- .compatible = "ti,da830-mcasp-audio",
- .data = &da830_mcasp_pdata,
- },
- {
- .compatible = "ti,am33xx-mcasp-audio",
- .data = &am33xx_mcasp_pdata,
- },
- {
- .compatible = "ti,dra7-mcasp-audio",
- .data = &dra7_mcasp_pdata,
- },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mcasp_dt_ids);
-
-static int mcasp_reparent_fck(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- struct clk *gfclk, *parent_clk;
- const char *parent_name;
- int ret;
-
- if (!node)
- return 0;
-
- parent_name = of_get_property(node, "fck_parent", NULL);
- if (!parent_name)
- return 0;
-
- dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n");
-
- gfclk = clk_get(&pdev->dev, "fck");
- if (IS_ERR(gfclk)) {
- dev_err(&pdev->dev, "failed to get fck\n");
- return PTR_ERR(gfclk);
- }
-
- parent_clk = clk_get(NULL, parent_name);
- if (IS_ERR(parent_clk)) {
- dev_err(&pdev->dev, "failed to get parent clock\n");
- ret = PTR_ERR(parent_clk);
- goto err1;
- }
-
- ret = clk_set_parent(gfclk, parent_clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to reparent fck\n");
- goto err2;
- }
-
-err2:
- clk_put(parent_clk);
-err1:
- clk_put(gfclk);
- return ret;
-}
-
-static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
- struct platform_device *pdev)
-{
- struct device_node *np = pdev->dev.of_node;
- struct davinci_mcasp_pdata *pdata = NULL;
- const struct of_device_id *match =
- of_match_device(mcasp_dt_ids, &pdev->dev);
- struct of_phandle_args dma_spec;
-
- const u32 *of_serial_dir32;
- u32 val;
- int i, ret = 0;
-
- if (pdev->dev.platform_data) {
- pdata = pdev->dev.platform_data;
- pdata->dismod = DISMOD_LOW;
- return pdata;
- } else if (match) {
- pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
- GFP_KERNEL);
- if (!pdata) {
- ret = -ENOMEM;
- return pdata;
- }
- } else {
- /* control shouldn't reach here. something is wrong */
- ret = -EINVAL;
- goto nodata;
- }
-
- ret = of_property_read_u32(np, "op-mode", &val);
- if (ret >= 0)
- pdata->op_mode = val;
-
- ret = of_property_read_u32(np, "tdm-slots", &val);
- if (ret >= 0) {
- if (val < 2 || val > 32) {
- dev_err(&pdev->dev,
- "tdm-slots must be in rage [2-32]\n");
- ret = -EINVAL;
- goto nodata;
- }
-
- pdata->tdm_slots = val;
- }
-
- of_serial_dir32 = of_get_property(np, "serial-dir", &val);
- val /= sizeof(u32);
- if (of_serial_dir32) {
- u8 *of_serial_dir = devm_kzalloc(&pdev->dev,
- (sizeof(*of_serial_dir) * val),
- GFP_KERNEL);
- if (!of_serial_dir) {
- ret = -ENOMEM;
- goto nodata;
- }
-
- for (i = 0; i < val; i++)
- of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]);
-
- pdata->num_serializer = val;
- pdata->serial_dir = of_serial_dir;
- }
-
- ret = of_property_match_string(np, "dma-names", "tx");
- if (ret < 0)
- goto nodata;
-
- ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret,
- &dma_spec);
- if (ret < 0)
- goto nodata;
-
- pdata->tx_dma_channel = dma_spec.args[0];
-
- /* RX is not valid in DIT mode */
- if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE) {
- ret = of_property_match_string(np, "dma-names", "rx");
- if (ret < 0)
- goto nodata;
-
- ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret,
- &dma_spec);
- if (ret < 0)
- goto nodata;
-
- pdata->rx_dma_channel = dma_spec.args[0];
- }
-
- ret = of_property_read_u32(np, "tx-num-evt", &val);
- if (ret >= 0)
- pdata->txnumevt = val;
-
- ret = of_property_read_u32(np, "rx-num-evt", &val);
- if (ret >= 0)
- pdata->rxnumevt = val;
-
- ret = of_property_read_u32(np, "sram-size-playback", &val);
- if (ret >= 0)
- pdata->sram_size_playback = val;
-
- ret = of_property_read_u32(np, "sram-size-capture", &val);
- if (ret >= 0)
- pdata->sram_size_capture = val;
-
- ret = of_property_read_u32(np, "dismod", &val);
- if (ret >= 0) {
- if (val == 0 || val == 2 || val == 3) {
- pdata->dismod = DISMOD_VAL(val);
- } else {
- dev_warn(&pdev->dev, "Invalid dismod value: %u\n", val);
- pdata->dismod = DISMOD_LOW;
- }
- } else {
- pdata->dismod = DISMOD_LOW;
- }
-
- return pdata;
-
-nodata:
- if (ret < 0) {
- dev_err(&pdev->dev, "Error populating platform data, err %d\n",
- ret);
- pdata = NULL;
- }
- return pdata;
-}
-
-enum {
- PCM_EDMA,
- PCM_SDMA,
-};
-static const char *sdma_prefix = "ti,omap";
-
-static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
-{
- struct dma_chan *chan;
- const char *tmp;
- int ret = PCM_EDMA;
-
- if (!mcasp->dev->of_node)
- return PCM_EDMA;
-
- tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data;
- chan = dma_request_slave_channel_reason(mcasp->dev, tmp);
- if (IS_ERR(chan)) {
- if (PTR_ERR(chan) != -EPROBE_DEFER)
- dev_err(mcasp->dev,
- "Can't verify DMA configuration (%ld)\n",
- PTR_ERR(chan));
- return PTR_ERR(chan);
- }
- if (WARN_ON(!chan->device || !chan->device->dev))
- return -EINVAL;
-
- if (chan->device->dev->of_node)
- ret = of_property_read_string(chan->device->dev->of_node,
- "compatible", &tmp);
- else
- dev_dbg(mcasp->dev, "DMA controller has no of-node\n");
-
- dma_release_channel(chan);
- if (ret)
- return ret;
-
- dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp);
- if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix)))
- return PCM_SDMA;
-
- return PCM_EDMA;
-}
-
-static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata)
-{
- int i;
- u32 offset = 0;
-
- if (pdata->version != MCASP_VERSION_4)
- return pdata->tx_dma_offset;
-
- for (i = 0; i < pdata->num_serializer; i++) {
- if (pdata->serial_dir[i] == TX_MODE) {
- if (!offset) {
- offset = DAVINCI_MCASP_TXBUF_REG(i);
- } else {
- pr_err("%s: Only one serializer allowed!\n",
- __func__);
- break;
- }
- }
- }
-
- return offset;
-}
-
-static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
-{
- int i;
- u32 offset = 0;
-
- if (pdata->version != MCASP_VERSION_4)
- return pdata->rx_dma_offset;
-
- for (i = 0; i < pdata->num_serializer; i++) {
- if (pdata->serial_dir[i] == RX_MODE) {
- if (!offset) {
- offset = DAVINCI_MCASP_RXBUF_REG(i);
- } else {
- pr_err("%s: Only one serializer allowed!\n",
- __func__);
- break;
- }
- }
- }
-
- return offset;
-}
-
-static int davinci_mcasp_probe(struct platform_device *pdev)
-{
- struct snd_dmaengine_dai_dma_data *dma_data;
- struct resource *mem, *res, *dat;
- struct davinci_mcasp_pdata *pdata;
- struct davinci_mcasp *mcasp;
- char *irq_name;
- int *dma;
- int irq;
- int ret;
-
- if (!pdev->dev.platform_data && !pdev->dev.of_node) {
- dev_err(&pdev->dev, "No platform data supplied\n");
- return -EINVAL;
- }
-
- mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp),
- GFP_KERNEL);
- if (!mcasp)
- return -ENOMEM;
-
- pdata = davinci_mcasp_set_pdata_from_of(pdev);
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data\n");
- return -EINVAL;
- }
-
- mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (!mem) {
- dev_warn(mcasp->dev,
- "\"mpu\" mem resource not found, using index 0\n");
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- return -ENODEV;
- }
- }
-
- mcasp->base = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(mcasp->base))
- return PTR_ERR(mcasp->base);
-
- pm_runtime_enable(&pdev->dev);
-
- mcasp->op_mode = pdata->op_mode;
- /* sanity check for tdm slots parameter */
- if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
- if (pdata->tdm_slots < 2) {
- dev_err(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 2;
- } else if (pdata->tdm_slots > 32) {
- dev_err(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 32;
- } else {
- mcasp->tdm_slots = pdata->tdm_slots;
- }
- }
-
- mcasp->num_serializer = pdata->num_serializer;
-#ifdef CONFIG_PM_SLEEP
- mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev,
- mcasp->num_serializer, sizeof(u32),
- GFP_KERNEL);
- if (!mcasp->context.xrsr_regs) {
- ret = -ENOMEM;
- goto err;
- }
-#endif
- mcasp->serial_dir = pdata->serial_dir;
- mcasp->version = pdata->version;
- mcasp->txnumevt = pdata->txnumevt;
- mcasp->rxnumevt = pdata->rxnumevt;
- mcasp->dismod = pdata->dismod;
-
- mcasp->dev = &pdev->dev;
-
- irq = platform_get_irq_byname(pdev, "common");
- if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common",
- dev_name(&pdev->dev));
- if (!irq_name) {
- ret = -ENOMEM;
- goto err;
- }
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- davinci_mcasp_common_irq_handler,
- IRQF_ONESHOT | IRQF_SHARED,
- irq_name, mcasp);
- if (ret) {
- dev_err(&pdev->dev, "common IRQ request failed\n");
- goto err;
- }
-
- mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
- mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
- }
-
- irq = platform_get_irq_byname(pdev, "rx");
- if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx",
- dev_name(&pdev->dev));
- if (!irq_name) {
- ret = -ENOMEM;
- goto err;
- }
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- davinci_mcasp_rx_irq_handler,
- IRQF_ONESHOT, irq_name, mcasp);
- if (ret) {
- dev_err(&pdev->dev, "RX IRQ request failed\n");
- goto err;
- }
-
- mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
- }
-
- irq = platform_get_irq_byname(pdev, "tx");
- if (irq >= 0) {
- irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx",
- dev_name(&pdev->dev));
- if (!irq_name) {
- ret = -ENOMEM;
- goto err;
- }
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- davinci_mcasp_tx_irq_handler,
- IRQF_ONESHOT, irq_name, mcasp);
- if (ret) {
- dev_err(&pdev->dev, "TX IRQ request failed\n");
- goto err;
- }
-
- mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
- }
-
- dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
- if (dat)
- mcasp->dat_port = true;
-
- dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- if (dat)
- dma_data->addr = dat->start;
- else
- dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata);
-
- dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (res)
- *dma = res->start;
- else
- *dma = pdata->tx_dma_channel;
-
- /* dmaengine filter data for DT and non-DT boot */
- if (pdev->dev.of_node)
- dma_data->filter_data = "tx";
- else
- dma_data->filter_data = dma;
-
- /* RX is not valid in DIT mode */
- if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
- dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
- if (dat)
- dma_data->addr = dat->start;
- else
- dma_data->addr =
- mem->start + davinci_mcasp_rxdma_offset(pdata);
-
- dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (res)
- *dma = res->start;
- else
- *dma = pdata->rx_dma_channel;
-
- /* dmaengine filter data for DT and non-DT boot */
- if (pdev->dev.of_node)
- dma_data->filter_data = "rx";
- else
- dma_data->filter_data = dma;
- }
-
- if (mcasp->version < MCASP_VERSION_3) {
- mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE;
- /* dma_params->dma_addr is pointing to the data port address */
- mcasp->dat_port = true;
- } else {
- mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
- }
-
- /* Allocate memory for long enough list for all possible
- * scenarios. Maximum number tdm slots is 32 and there cannot
- * be more serializers than given in the configuration. The
- * serializer directions could be taken into account, but it
- * would make code much more complex and save only couple of
- * bytes.
- */
- mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
- devm_kcalloc(mcasp->dev,
- 32 + mcasp->num_serializer - 1,
- sizeof(unsigned int),
- GFP_KERNEL);
-
- mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
- devm_kcalloc(mcasp->dev,
- 32 + mcasp->num_serializer - 1,
- sizeof(unsigned int),
- GFP_KERNEL);
-
- if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
- !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) {
- ret = -ENOMEM;
- goto err;
- }
-
- ret = davinci_mcasp_set_ch_constraints(mcasp);
- if (ret)
- goto err;
-
- dev_set_drvdata(&pdev->dev, mcasp);
-
- mcasp_reparent_fck(pdev);
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &davinci_mcasp_component,
- &davinci_mcasp_dai[pdata->op_mode], 1);
-
- if (ret != 0)
- goto err;
-
- ret = davinci_mcasp_get_dma_type(mcasp);
- switch (ret) {
- case PCM_EDMA:
-#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \
- (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
- IS_MODULE(CONFIG_SND_EDMA_SOC))
- ret = edma_pcm_platform_register(&pdev->dev);
-#else
- dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
- ret = -EINVAL;
- goto err;
-#endif
- break;
- case PCM_SDMA:
-#if IS_BUILTIN(CONFIG_SND_SDMA_SOC) || \
- (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
- IS_MODULE(CONFIG_SND_SDMA_SOC))
- ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
-#else
- dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
- ret = -EINVAL;
- goto err;
-#endif
- break;
- default:
- dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
- case -EPROBE_DEFER:
- goto err;
- break;
- }
-
- if (ret) {
- dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
- goto err;
- }
-
- return 0;
-
-err:
- pm_runtime_disable(&pdev->dev);
- return ret;
-}
-
-static int davinci_mcasp_remove(struct platform_device *pdev)
-{
- pm_runtime_disable(&pdev->dev);
-
- return 0;
-}
-
-static struct platform_driver davinci_mcasp_driver = {
- .probe = davinci_mcasp_probe,
- .remove = davinci_mcasp_remove,
- .driver = {
- .name = "davinci-mcasp",
- .of_match_table = mcasp_dt_ids,
- },
-};
-
-module_platform_driver(davinci_mcasp_driver);
-
-MODULE_AUTHOR("Steve Chen");
-MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * ALSA SoC McASP Audio Layer for TI DAVINCI processor
- *
- * MCASP related definitions
- *
- * Author: Nirmal Pandey <n-pandey@ti.com>,
- * Suresh Rajashekara <suresh.r@ti.com>
- * Steve Chen <schen@.mvista.com>
- *
- * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
- * Copyright: (C) 2009 Texas Instruments, India
- *
- * 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 DAVINCI_MCASP_H
-#define DAVINCI_MCASP_H
-
-/*
- * McASP register definitions
- */
-#define DAVINCI_MCASP_PID_REG 0x00
-#define DAVINCI_MCASP_PWREMUMGT_REG 0x04
-
-#define DAVINCI_MCASP_PFUNC_REG 0x10
-#define DAVINCI_MCASP_PDIR_REG 0x14
-#define DAVINCI_MCASP_PDOUT_REG 0x18
-#define DAVINCI_MCASP_PDSET_REG 0x1c
-
-#define DAVINCI_MCASP_PDCLR_REG 0x20
-
-#define DAVINCI_MCASP_TLGC_REG 0x30
-#define DAVINCI_MCASP_TLMR_REG 0x34
-
-#define DAVINCI_MCASP_GBLCTL_REG 0x44
-#define DAVINCI_MCASP_AMUTE_REG 0x48
-#define DAVINCI_MCASP_LBCTL_REG 0x4c
-
-#define DAVINCI_MCASP_TXDITCTL_REG 0x50
-
-#define DAVINCI_MCASP_GBLCTLR_REG 0x60
-#define DAVINCI_MCASP_RXMASK_REG 0x64
-#define DAVINCI_MCASP_RXFMT_REG 0x68
-#define DAVINCI_MCASP_RXFMCTL_REG 0x6c
-
-#define DAVINCI_MCASP_ACLKRCTL_REG 0x70
-#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74
-#define DAVINCI_MCASP_RXTDM_REG 0x78
-#define DAVINCI_MCASP_EVTCTLR_REG 0x7c
-
-#define DAVINCI_MCASP_RXSTAT_REG 0x80
-#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84
-#define DAVINCI_MCASP_RXCLKCHK_REG 0x88
-#define DAVINCI_MCASP_REVTCTL_REG 0x8c
-
-#define DAVINCI_MCASP_GBLCTLX_REG 0xa0
-#define DAVINCI_MCASP_TXMASK_REG 0xa4
-#define DAVINCI_MCASP_TXFMT_REG 0xa8
-#define DAVINCI_MCASP_TXFMCTL_REG 0xac
-
-#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0
-#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4
-#define DAVINCI_MCASP_TXTDM_REG 0xb8
-#define DAVINCI_MCASP_EVTCTLX_REG 0xbc
-
-#define DAVINCI_MCASP_TXSTAT_REG 0xc0
-#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4
-#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8
-#define DAVINCI_MCASP_XEVTCTL_REG 0xcc
-
-/* Left(even TDM Slot) Channel Status Register File */
-#define DAVINCI_MCASP_DITCSRA_REG 0x100
-/* Right(odd TDM slot) Channel Status Register File */
-#define DAVINCI_MCASP_DITCSRB_REG 0x118
-/* Left(even TDM slot) User Data Register File */
-#define DAVINCI_MCASP_DITUDRA_REG 0x130
-/* Right(odd TDM Slot) User Data Register File */
-#define DAVINCI_MCASP_DITUDRB_REG 0x148
-
-/* Serializer n Control Register */
-#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
-#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
- (n << 2))
-
-/* Transmit Buffer for Serializer n */
-#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2))
-/* Receive Buffer for Serializer n */
-#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2))
-
-/* McASP FIFO Registers */
-#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
-#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000)
-
-/* FIFO register offsets from AFIFO base */
-#define MCASP_WFIFOCTL_OFFSET (0x0)
-#define MCASP_WFIFOSTS_OFFSET (0x4)
-#define MCASP_RFIFOCTL_OFFSET (0x8)
-#define MCASP_RFIFOSTS_OFFSET (0xc)
-
-/*
- * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
- * Register Bits
- */
-#define MCASP_FREE BIT(0)
-#define MCASP_SOFT BIT(1)
-
-/*
- * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
- * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
- * DAVINCI_MCASP_PDOUT_REG - Pin output in GPIO mode
- * DAVINCI_MCASP_PDSET_REG - Pin input in GPIO mode
- */
-#define PIN_BIT_AXR(n) (n)
-#define PIN_BIT_AMUTE 25
-#define PIN_BIT_ACLKX 26
-#define PIN_BIT_AHCLKX 27
-#define PIN_BIT_AFSX 28
-#define PIN_BIT_ACLKR 29
-#define PIN_BIT_AHCLKR 30
-#define PIN_BIT_AFSR 31
-
-/*
- * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
- */
-#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
-#define VA BIT(2)
-#define VB BIT(3)
-
-/*
- * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
- */
-#define TXROT(val) (val)
-#define TXSEL BIT(3)
-#define TXSSZ(val) (val<<4)
-#define TXPBIT(val) (val<<8)
-#define TXPAD(val) (val<<13)
-#define TXORD BIT(15)
-#define FSXDLY(val) (val<<16)
-
-/*
- * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
- */
-#define RXROT(val) (val)
-#define RXSEL BIT(3)
-#define RXSSZ(val) (val<<4)
-#define RXPBIT(val) (val<<8)
-#define RXPAD(val) (val<<13)
-#define RXORD BIT(15)
-#define FSRDLY(val) (val<<16)
-
-/*
- * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
- */
-#define FSXPOL BIT(0)
-#define AFSXE BIT(1)
-#define FSXDUR BIT(4)
-#define FSXMOD(val) (val<<7)
-
-/*
- * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
- */
-#define FSRPOL BIT(0)
-#define AFSRE BIT(1)
-#define FSRDUR BIT(4)
-#define FSRMOD(val) (val<<7)
-
-/*
- * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
- */
-#define ACLKXDIV(val) (val)
-#define ACLKXE BIT(5)
-#define TX_ASYNC BIT(6)
-#define ACLKXPOL BIT(7)
-#define ACLKXDIV_MASK 0x1f
-
-/*
- * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
- */
-#define ACLKRDIV(val) (val)
-#define ACLKRE BIT(5)
-#define RX_ASYNC BIT(6)
-#define ACLKRPOL BIT(7)
-#define ACLKRDIV_MASK 0x1f
-
-/*
- * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
- * Register Bits
- */
-#define AHCLKXDIV(val) (val)
-#define AHCLKXPOL BIT(14)
-#define AHCLKXE BIT(15)
-#define AHCLKXDIV_MASK 0xfff
-
-/*
- * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
- * Register Bits
- */
-#define AHCLKRDIV(val) (val)
-#define AHCLKRPOL BIT(14)
-#define AHCLKRE BIT(15)
-#define AHCLKRDIV_MASK 0xfff
-
-/*
- * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
- */
-#define MODE(val) (val)
-#define DISMOD_3STATE (0x0)
-#define DISMOD_LOW (0x2 << 2)
-#define DISMOD_HIGH (0x3 << 2)
-#define DISMOD_VAL(x) ((x) << 2)
-#define DISMOD_MASK DISMOD_HIGH
-#define TXSTATE BIT(4)
-#define RXSTATE BIT(5)
-#define SRMOD_MASK 3
-#define SRMOD_INACTIVE 0
-
-/*
- * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
- */
-#define LBEN BIT(0)
-#define LBORD BIT(1)
-#define LBGENMODE(val) (val<<2)
-
-/*
- * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
- */
-#define TXTDMS(n) (1<<n)
-
-/*
- * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
- */
-#define RXTDMS(n) (1<<n)
-
-/*
- * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits
- */
-#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */
-#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */
-#define RXSERCLR BIT(2) /* Receiver Serializer Clear */
-#define RXSMRST BIT(3) /* Receiver State Machine Reset */
-#define RXFSRST BIT(4) /* Frame Sync Generator Reset */
-#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
-#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
-#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
-#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
-#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
-
-/*
- * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
- * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
- */
-#define XRERR BIT(8) /* Transmit/Receive error */
-#define XRDATA BIT(5) /* Transmit/Receive data ready */
-
-/*
- * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
- */
-#define MUTENA(val) (val)
-#define MUTEINPOL BIT(2)
-#define MUTEINENA BIT(3)
-#define MUTEIN BIT(4)
-#define MUTER BIT(5)
-#define MUTEX BIT(6)
-#define MUTEFSR BIT(7)
-#define MUTEFSX BIT(8)
-#define MUTEBADCLKR BIT(9)
-#define MUTEBADCLKX BIT(10)
-#define MUTERXDMAERR BIT(11)
-#define MUTETXDMAERR BIT(12)
-
-/*
- * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
- */
-#define RXDATADMADIS BIT(0)
-
-/*
- * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
- */
-#define TXDATADMADIS BIT(0)
-
-/*
- * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
- */
-#define ROVRN BIT(0)
-
-/*
- * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
- */
-#define XUNDRN BIT(0)
-
-/*
- * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
- */
-#define FIFO_ENABLE BIT(16)
-#define NUMEVT_MASK (0xFF << 8)
-#define NUMEVT(x) (((x) & 0xFF) << 8)
-#define NUMDMA_MASK (0xFF)
-
-/* clock divider IDs */
-#define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */
-#define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */
-#define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */
-
-#endif /* DAVINCI_MCASP_H */
+++ /dev/null
-/*
- * ALSA SoC Voice Codec Interface for TI DAVINCI processor
- *
- * Copyright (C) 2010 Texas Instruments.
- *
- * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/mfd/davinci_voicecodec.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "edma-pcm.h"
-#include "davinci-i2s.h"
-
-#define MOD_REG_BIT(val, mask, set) do { \
- if (set) { \
- val |= mask; \
- } else { \
- val &= ~mask; \
- } \
-} while (0)
-
-struct davinci_vcif_dev {
- struct davinci_vc *davinci_vc;
- struct snd_dmaengine_dai_dma_data dma_data[2];
- int dma_request[2];
-};
-
-static void davinci_vcif_start(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_vcif_dev *davinci_vcif_dev =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- u32 w;
-
- /* Start the sample generator and enable transmitter/receiver */
- w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
- else
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
-
- writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
-}
-
-static void davinci_vcif_stop(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct davinci_vcif_dev *davinci_vcif_dev =
- snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- u32 w;
-
- /* Reset transmitter/receiver and sample rate/frame sync generators */
- w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
- else
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
-
- writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
-}
-
-static int davinci_vcif_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai);
- struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
- u32 w;
-
- /* Restart the codec before setup */
- davinci_vcif_stop(substream);
- davinci_vcif_start(substream);
-
- /* General line settings */
- writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL);
-
- writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR);
-
- writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN);
-
- w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
-
- /* Determine xfer data type */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_U8:
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
- DAVINCI_VC_CTRL_RD_UNSIGNED |
- DAVINCI_VC_CTRL_WD_BITS_8 |
- DAVINCI_VC_CTRL_WD_UNSIGNED, 1);
- break;
- case SNDRV_PCM_FORMAT_S8:
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
- DAVINCI_VC_CTRL_WD_BITS_8, 1);
-
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED |
- DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
- break;
- case SNDRV_PCM_FORMAT_S16_LE:
- MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
- DAVINCI_VC_CTRL_RD_UNSIGNED |
- DAVINCI_VC_CTRL_WD_BITS_8 |
- DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
- break;
- default:
- printk(KERN_WARNING "davinci-vcif: unsupported PCM format");
- return -EINVAL;
- }
-
- writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
-
- return 0;
-}
-
-static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- int ret = 0;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- davinci_vcif_start(substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- davinci_vcif_stop(substream);
- break;
- default:
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
-
-static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
- .trigger = davinci_vcif_trigger,
- .hw_params = davinci_vcif_hw_params,
-};
-
-static int davinci_vcif_dai_probe(struct snd_soc_dai *dai)
-{
- struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
-
- return 0;
-}
-
-static struct snd_soc_dai_driver davinci_vcif_dai = {
- .probe = davinci_vcif_dai_probe,
- .playback = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = DAVINCI_VCIF_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .capture = {
- .channels_min = 1,
- .channels_max = 2,
- .rates = DAVINCI_VCIF_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = &davinci_vcif_dai_ops,
-
-};
-
-static const struct snd_soc_component_driver davinci_vcif_component = {
- .name = "davinci-vcif",
-};
-
-static int davinci_vcif_probe(struct platform_device *pdev)
-{
- struct davinci_vc *davinci_vc = pdev->dev.platform_data;
- struct davinci_vcif_dev *davinci_vcif_dev;
- int ret;
-
- davinci_vcif_dev = devm_kzalloc(&pdev->dev,
- sizeof(struct davinci_vcif_dev),
- GFP_KERNEL);
- if (!davinci_vcif_dev)
- return -ENOMEM;
-
- /* DMA tx params */
- davinci_vcif_dev->davinci_vc = davinci_vc;
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data =
- &davinci_vc->davinci_vcif.dma_tx_channel;
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
- davinci_vc->davinci_vcif.dma_tx_addr;
-
- /* DMA rx params */
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data =
- &davinci_vc->davinci_vcif.dma_rx_channel;
- davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
- davinci_vc->davinci_vcif.dma_rx_addr;
-
- dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &davinci_vcif_component,
- &davinci_vcif_dai, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "could not register dai\n");
- return ret;
- }
-
- ret = edma_pcm_platform_register(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
- return ret;
- }
-
- return 0;
-}
-
-static struct platform_driver davinci_vcif_driver = {
- .probe = davinci_vcif_probe,
- .driver = {
- .name = "davinci-vcif",
- },
-};
-
-module_platform_driver(davinci_vcif_driver);
-
-MODULE_AUTHOR("Miguel Aguilar");
-MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * edma-pcm.c - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
- *
- * Copyright (C) 2014 Texas Instruments, Inc.
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Based on: sound/soc/tegra/tegra_pcm.c
- *
- * 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.
- */
-
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-#include <linux/edma.h>
-
-#include "edma-pcm.h"
-
-static const struct snd_pcm_hardware edma_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
- SNDRV_PCM_INFO_INTERLEAVED,
- .buffer_bytes_max = 128 * 1024,
- .period_bytes_min = 32,
- .period_bytes_max = 64 * 1024,
- .periods_min = 2,
- .periods_max = 19, /* Limit by edma dmaengine driver */
-};
-
-static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = {
- .pcm_hardware = &edma_pcm_hardware,
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .compat_filter_fn = edma_filter_fn,
- .prealloc_buffer_size = 128 * 1024,
-};
-
-int edma_pcm_platform_register(struct device *dev)
-{
- return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_COMPAT);
-}
-EXPORT_SYMBOL_GPL(edma_pcm_platform_register);
-
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_DESCRIPTION("eDMA PCM ASoC platform driver");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
- *
- * Copyright (C) 2014 Texas Instruments, Inc.
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * Based on: sound/soc/tegra/tegra_pcm.h
- *
- * 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.
- */
-
-#ifndef __EDMA_PCM_H__
-#define __EDMA_PCM_H__
-
-#if IS_ENABLED(CONFIG_SND_EDMA_SOC)
-int edma_pcm_platform_register(struct device *dev);
-#else
-static inline int edma_pcm_platform_register(struct device *dev)
-{
- return 0;
-}
-#endif /* CONFIG_SND_EDMA_SOC */
-
-#endif /* __EDMA_PCM_H__ */
+++ /dev/null
-config SND_OMAP_SOC
- tristate "SoC Audio for Texas Instruments OMAP chips (deprecated)"
- depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST)
- select SND_SDMA_SOC
-
-config SND_SDMA_SOC
- tristate "SoC Audio for Texas Instruments chips using sDMA"
- depends on DMA_OMAP || COMPILE_TEST
- select SND_SOC_GENERIC_DMAENGINE_PCM
-
-config SND_OMAP_SOC_DMIC
- tristate
-
-config SND_OMAP_SOC_MCBSP
- tristate
-
-config SND_OMAP_SOC_MCPDM
- tristate
-
-config SND_OMAP_SOC_HDMI_AUDIO
- tristate "HDMI audio support for OMAP4+ based SoCs"
- depends on SND_SDMA_SOC
- help
- For HDMI audio to work OMAPDSS HDMI support should be
- enabled.
- The hdmi audio driver implements cpu-dai component using the
- callbacks provided by OMAPDSS and registers the component
- under DSS HDMI device. Omap-pcm is registered for platform
- component also under DSS HDMI device. Dummy codec is used as
- as codec component. The hdmi audio driver implements also
- the card and registers it under its own platform device.
- The device for the driver is registered by OMAPDSS hdmi
- driver.
-
-config SND_OMAP_SOC_N810
- tristate "SoC Audio support for Nokia N810"
- depends on SND_SDMA_SOC && MACH_NOKIA_N810 && I2C
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC3X
- help
- Say Y if you want to add support for SoC audio on Nokia N810.
-
-config SND_OMAP_SOC_RX51
- tristate "SoC Audio support for Nokia N900 (RX-51)"
- depends on SND_SDMA_SOC && ARM && I2C
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC3X
- select SND_SOC_TPA6130A2
- depends on GPIOLIB
- help
- Say Y if you want to add support for SoC audio on Nokia N900
- cellphone.
-
-config SND_OMAP_SOC_AMS_DELTA
- tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
- depends on SND_SDMA_SOC && MACH_AMS_DELTA && TTY
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_CX20442
- help
- Say Y if you want to add support for SoC audio device connected to
- a handset and a speakerphone found on Amstrad E3 (Delta) videophone.
-
- Note that in order to get those devices fully supported, you have to
- build the kernel with standard serial port driver included and
- configured for at least 4 ports. Then, from userspace, you must load
- a line discipline #19 on the modem (ttyS3) serial line. The simplest
- way to achieve this is to install util-linux-ng and use the included
- ldattach utility. This can be started automatically from udev,
- a simple rule like this one should do the trick (it does for me):
- ACTION=="add", KERNEL=="controlC0", \
- RUN+="/usr/sbin/ldattach 19 /dev/ttyS3"
-
-config SND_OMAP_SOC_OSK5912
- tristate "SoC Audio support for omap osk5912"
- depends on SND_SDMA_SOC && MACH_OMAP_OSK && I2C
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TLV320AIC23_I2C
- help
- Say Y if you want to add support for SoC audio on osk5912.
-
-config SND_OMAP_SOC_OMAP_TWL4030
- tristate "SoC Audio support for TI SoC based boards with twl4030 codec"
- depends on TWL4030_CORE && SND_SDMA_SOC
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on TI SoC based boards
- using twl4030 as c codec. This driver currently supports:
- - Beagleboard or Devkit8000
- - Gumstix Overo or CompuLab CM-T35/CM-T3730
- - IGEP v2
- - OMAP3EVM
- - SDP3430
- - Zoom2
-
-config SND_OMAP_SOC_OMAP_ABE_TWL6040
- tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
- depends on TWL6040_CORE && SND_SDMA_SOC && COMMON_CLK
- depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
- select SND_OMAP_SOC_DMIC
- select SND_OMAP_SOC_MCPDM
- select SND_SOC_TWL6040
- select SND_SOC_DMIC
- select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS)
- select CLK_TWL6040
- help
- Say Y if you want to add support for SoC audio on OMAP boards using
- ABE and twl6040 codec. This driver currently supports:
- - SDP4430/Blaze boards
- - PandaBoard (4430)
- - PandaBoardES (4460)
- - omap5-uevm (5432)
-
-config SND_OMAP_SOC_OMAP3_PANDORA
- tristate "SoC Audio support for OMAP3 Pandora"
- depends on TWL4030_CORE && SND_SDMA_SOC && MACH_OMAP3_PANDORA
- select SND_OMAP_SOC_MCBSP
- select SND_SOC_TWL4030
- help
- Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
+++ /dev/null
-# SPDX-License-Identifier: GPL-2.0
-# OMAP Platform Support
-snd-soc-sdma-objs := sdma-pcm.o
-snd-soc-omap-dmic-objs := omap-dmic.o
-snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o
-snd-soc-omap-mcpdm-objs := omap-mcpdm.o
-snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o
-
-obj-$(CONFIG_SND_SDMA_SOC) += snd-soc-sdma.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
-obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o
-
-# OMAP Machine Support
-snd-soc-n810-objs := n810.o
-snd-soc-rx51-objs := rx51.o
-snd-soc-ams-delta-objs := ams-delta.o
-snd-soc-osk5912-objs := osk5912.o
-snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
-snd-soc-omap-twl4030-objs := omap-twl4030.o
-snd-soc-omap3pandora-objs := omap3pandora.o
-
-obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
-obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
-obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
-obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
-obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
+++ /dev/null
-/*
- * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone
- *
- * Copyright (C) 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * Initially based on sound/soc/omap/osk5912.x
- * Copyright (C) 2008 Mistral Solutions
- *
- * 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
- *
- */
-
-#include <linux/gpio/consumer.h>
-#include <linux/spinlock.h>
-#include <linux/tty.h>
-#include <linux/module.h>
-
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "../codecs/cx20442.h"
-
-/* Board specific DAPM widgets */
-static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
- /* Handset */
- SND_SOC_DAPM_MIC("Mouthpiece", NULL),
- SND_SOC_DAPM_HP("Earpiece", NULL),
- /* Handsfree/Speakerphone */
- SND_SOC_DAPM_MIC("Microphone", NULL),
- SND_SOC_DAPM_SPK("Speaker", NULL),
-};
-
-/* How they are connected to codec pins */
-static const struct snd_soc_dapm_route ams_delta_audio_map[] = {
- {"TELIN", NULL, "Mouthpiece"},
- {"Earpiece", NULL, "TELOUT"},
-
- {"MIC", NULL, "Microphone"},
- {"Speaker", NULL, "SPKOUT"},
-};
-
-/*
- * Controls, functional after the modem line discipline is activated.
- */
-
-/* Virtual switch: audio input/output constellations */
-static const char *ams_delta_audio_mode[] =
- {"Mixed", "Handset", "Handsfree", "Speakerphone"};
-
-/* Selection <-> pin translation */
-#define AMS_DELTA_MOUTHPIECE 0
-#define AMS_DELTA_EARPIECE 1
-#define AMS_DELTA_MICROPHONE 2
-#define AMS_DELTA_SPEAKER 3
-#define AMS_DELTA_AGC 4
-
-#define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \
- (1 << AMS_DELTA_MICROPHONE))
-#define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \
- (1 << AMS_DELTA_EARPIECE))
-#define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \
- (1 << AMS_DELTA_SPEAKER))
-#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
-
-static const unsigned short ams_delta_audio_mode_pins[] = {
- AMS_DELTA_MIXED,
- AMS_DELTA_HANDSET,
- AMS_DELTA_HANDSFREE,
- AMS_DELTA_SPEAKERPHONE,
-};
-
-static unsigned short ams_delta_audio_agc;
-
-/*
- * Used for passing a codec structure pointer
- * from the board initialization code to the tty line discipline.
- */
-static struct snd_soc_component *cx20442_codec;
-
-static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_context *dapm = &card->dapm;
- struct soc_enum *control = (struct soc_enum *)kcontrol->private_value;
- unsigned short pins;
- int pin, changed = 0;
-
- /* Refuse any mode changes if we are not able to control the codec. */
- if (!cx20442_codec->card->pop_time)
- return -EUNATCH;
-
- if (ucontrol->value.enumerated.item[0] >= control->items)
- return -EINVAL;
-
- snd_soc_dapm_mutex_lock(dapm);
-
- /* Translate selection to bitmap */
- pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]];
-
- /* Setup pins after corresponding bits if changed */
- pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE));
-
- if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) {
- changed = 1;
- if (pin)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
- }
- pin = !!(pins & (1 << AMS_DELTA_EARPIECE));
- if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) {
- changed = 1;
- if (pin)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece");
- }
- pin = !!(pins & (1 << AMS_DELTA_MICROPHONE));
- if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) {
- changed = 1;
- if (pin)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone");
- }
- pin = !!(pins & (1 << AMS_DELTA_SPEAKER));
- if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) {
- changed = 1;
- if (pin)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
- }
- pin = !!(pins & (1 << AMS_DELTA_AGC));
- if (pin != ams_delta_audio_agc) {
- ams_delta_audio_agc = pin;
- changed = 1;
- if (pin)
- snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
- }
-
- if (changed)
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-
- return changed;
-}
-
-static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct snd_soc_dapm_context *dapm = &card->dapm;
- unsigned short pins, mode;
-
- pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") <<
- AMS_DELTA_MOUTHPIECE) |
- (snd_soc_dapm_get_pin_status(dapm, "Earpiece") <<
- AMS_DELTA_EARPIECE));
- if (pins)
- pins |= (snd_soc_dapm_get_pin_status(dapm, "Microphone") <<
- AMS_DELTA_MICROPHONE);
- else
- pins = ((snd_soc_dapm_get_pin_status(dapm, "Microphone") <<
- AMS_DELTA_MICROPHONE) |
- (snd_soc_dapm_get_pin_status(dapm, "Speaker") <<
- AMS_DELTA_SPEAKER) |
- (ams_delta_audio_agc << AMS_DELTA_AGC));
-
- for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++)
- if (pins == ams_delta_audio_mode_pins[mode])
- break;
-
- if (mode >= ARRAY_SIZE(ams_delta_audio_mode))
- return -EINVAL;
-
- ucontrol->value.enumerated.item[0] = mode;
-
- return 0;
-}
-
-static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum,
- ams_delta_audio_mode);
-
-static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
- SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum,
- ams_delta_get_audio_mode, ams_delta_set_audio_mode),
-};
-
-/* Hook switch */
-static struct snd_soc_jack ams_delta_hook_switch;
-static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = {
- {
- .name = "hook_switch",
- .report = SND_JACK_HEADSET,
- .invert = 1,
- .debounce_time = 150,
- }
-};
-
-/* After we are able to control the codec over the modem,
- * the hook switch can be used for dynamic DAPM reconfiguration. */
-static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = {
- /* Handset */
- {
- .pin = "Mouthpiece",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Earpiece",
- .mask = SND_JACK_HEADPHONE,
- },
- /* Handsfree */
- {
- .pin = "Microphone",
- .mask = SND_JACK_MICROPHONE,
- .invert = 1,
- },
- {
- .pin = "Speaker",
- .mask = SND_JACK_HEADPHONE,
- .invert = 1,
- },
-};
-
-
-/*
- * Modem line discipline, required for making above controls functional.
- * Activated from userspace with ldattach, possibly invoked from udev rule.
- */
-
-/* To actually apply any modem controlled configuration changes to the codec,
- * we must connect codec DAI pins to the modem for a moment. Be careful not
- * to interfere with our digital mute function that shares the same hardware. */
-static struct timer_list cx81801_timer;
-static bool cx81801_cmd_pending;
-static bool ams_delta_muted;
-static DEFINE_SPINLOCK(ams_delta_lock);
-static struct gpio_desc *gpiod_modem_codec;
-
-static void cx81801_timeout(struct timer_list *unused)
-{
- int muted;
-
- spin_lock(&ams_delta_lock);
- cx81801_cmd_pending = 0;
- muted = ams_delta_muted;
- spin_unlock(&ams_delta_lock);
-
- /* Reconnect the codec DAI back from the modem to the CPU DAI
- * only if digital mute still off */
- if (!muted)
- gpiod_set_value(gpiod_modem_codec, 0);
-}
-
-/* Line discipline .open() */
-static int cx81801_open(struct tty_struct *tty)
-{
- int ret;
-
- if (!cx20442_codec)
- return -ENODEV;
-
- /*
- * Pass the codec structure pointer for use by other ldisc callbacks,
- * both the card and the codec specific parts.
- */
- tty->disc_data = cx20442_codec;
-
- ret = v253_ops.open(tty);
-
- if (ret < 0)
- tty->disc_data = NULL;
-
- return ret;
-}
-
-/* Line discipline .close() */
-static void cx81801_close(struct tty_struct *tty)
-{
- struct snd_soc_component *component = tty->disc_data;
- struct snd_soc_dapm_context *dapm = &component->card->dapm;
-
- del_timer_sync(&cx81801_timer);
-
- /* Prevent the hook switch from further changing the DAPM pins */
- INIT_LIST_HEAD(&ams_delta_hook_switch.pins);
-
- if (!component)
- return;
-
- v253_ops.close(tty);
-
- /* Revert back to default audio input/output constellation */
- snd_soc_dapm_mutex_lock(dapm);
-
- snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
- snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
- snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
- snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-}
-
-/* Line discipline .hangup() */
-static int cx81801_hangup(struct tty_struct *tty)
-{
- cx81801_close(tty);
- return 0;
-}
-
-/* Line discipline .receive_buf() */
-static void cx81801_receive(struct tty_struct *tty,
- const unsigned char *cp, char *fp, int count)
-{
- struct snd_soc_component *component = tty->disc_data;
- const unsigned char *c;
- int apply, ret;
-
- if (!component)
- return;
-
- if (!component->card->pop_time) {
- /* First modem response, complete setup procedure */
-
- /* Initialize timer used for config pulse generation */
- timer_setup(&cx81801_timer, cx81801_timeout, 0);
-
- v253_ops.receive_buf(tty, cp, fp, count);
-
- /* Link hook switch to DAPM pins */
- ret = snd_soc_jack_add_pins(&ams_delta_hook_switch,
- ARRAY_SIZE(ams_delta_hook_switch_pins),
- ams_delta_hook_switch_pins);
- if (ret)
- dev_warn(component->dev,
- "Failed to link hook switch to DAPM pins, "
- "will continue with hook switch unlinked.\n");
-
- return;
- }
-
- v253_ops.receive_buf(tty, cp, fp, count);
-
- for (c = &cp[count - 1]; c >= cp; c--) {
- if (*c != '\r')
- continue;
- /* Complete modem response received, apply config to codec */
-
- spin_lock_bh(&ams_delta_lock);
- mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150));
- apply = !ams_delta_muted && !cx81801_cmd_pending;
- cx81801_cmd_pending = 1;
- spin_unlock_bh(&ams_delta_lock);
-
- /* Apply config pulse by connecting the codec to the modem
- * if not already done */
- if (apply)
- gpiod_set_value(gpiod_modem_codec, 1);
- break;
- }
-}
-
-/* Line discipline .write_wakeup() */
-static void cx81801_wakeup(struct tty_struct *tty)
-{
- v253_ops.write_wakeup(tty);
-}
-
-static struct tty_ldisc_ops cx81801_ops = {
- .magic = TTY_LDISC_MAGIC,
- .name = "cx81801",
- .owner = THIS_MODULE,
- .open = cx81801_open,
- .close = cx81801_close,
- .hangup = cx81801_hangup,
- .receive_buf = cx81801_receive,
- .write_wakeup = cx81801_wakeup,
-};
-
-
-/*
- * Even if not very useful, the sound card can still work without any of the
- * above functonality activated. You can still control its audio input/output
- * constellation and speakerphone gain from userspace by issuing AT commands
- * over the modem port.
- */
-
-static struct snd_soc_ops ams_delta_ops;
-
-
-/* Digital mute implemented using modem/CPU multiplexer.
- * Shares hardware with codec config pulse generation */
-static bool ams_delta_muted = 1;
-
-static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute)
-{
- int apply;
-
- if (ams_delta_muted == mute)
- return 0;
-
- spin_lock_bh(&ams_delta_lock);
- ams_delta_muted = mute;
- apply = !cx81801_cmd_pending;
- spin_unlock_bh(&ams_delta_lock);
-
- if (apply)
- gpiod_set_value(gpiod_modem_codec, !!mute);
- return 0;
-}
-
-/* Our codec DAI probably doesn't have its own .ops structure */
-static const struct snd_soc_dai_ops ams_delta_dai_ops = {
- .digital_mute = ams_delta_digital_mute,
-};
-
-/* Will be used if the codec ever has its own digital_mute function */
-static int ams_delta_startup(struct snd_pcm_substream *substream)
-{
- return ams_delta_digital_mute(NULL, 0);
-}
-
-static void ams_delta_shutdown(struct snd_pcm_substream *substream)
-{
- ams_delta_digital_mute(NULL, 1);
-}
-
-
-/*
- * Card initialization
- */
-
-static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dapm_context *dapm = &card->dapm;
- int ret;
- /* Codec is ready, now add/activate board specific controls */
-
- /* Store a pointer to the codec structure for tty ldisc use */
- cx20442_codec = rtd->codec_dai->component;
-
- /* Add hook switch - can be used to control the codec from userspace
- * even if line discipline fails */
- ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET,
- &ams_delta_hook_switch, NULL, 0);
- if (ret)
- dev_warn(card->dev,
- "Failed to allocate resources for hook switch, "
- "will continue without one.\n");
- else {
- ret = snd_soc_jack_add_gpiods(card->dev, &ams_delta_hook_switch,
- ARRAY_SIZE(ams_delta_hook_switch_gpios),
- ams_delta_hook_switch_gpios);
- if (ret)
- dev_warn(card->dev,
- "Failed to set up hook switch GPIO line, "
- "will continue with hook switch inactive.\n");
- }
-
- gpiod_modem_codec = devm_gpiod_get(card->dev, "modem_codec",
- GPIOD_OUT_HIGH);
- if (IS_ERR(gpiod_modem_codec)) {
- dev_warn(card->dev, "Failed to obtain modem_codec GPIO\n");
- return 0;
- }
-
- /* Set up digital mute if not provided by the codec */
- if (!codec_dai->driver->ops) {
- codec_dai->driver->ops = &ams_delta_dai_ops;
- } else {
- ams_delta_ops.startup = ams_delta_startup;
- ams_delta_ops.shutdown = ams_delta_shutdown;
- }
-
- /* Register optional line discipline for over the modem control */
- ret = tty_register_ldisc(N_V253, &cx81801_ops);
- if (ret) {
- dev_warn(card->dev,
- "Failed to register line discipline, "
- "will continue without any controls.\n");
- return 0;
- }
-
- /* Set up initial pin constellation */
- snd_soc_dapm_disable_pin(dapm, "Mouthpiece");
- snd_soc_dapm_disable_pin(dapm, "Speaker");
- snd_soc_dapm_disable_pin(dapm, "AGCIN");
- snd_soc_dapm_disable_pin(dapm, "AGCOUT");
-
- return 0;
-}
-
-/* DAI glue - connects codec <--> CPU */
-static struct snd_soc_dai_link ams_delta_dai_link = {
- .name = "CX20442",
- .stream_name = "CX20442",
- .cpu_dai_name = "omap-mcbsp.1",
- .codec_dai_name = "cx20442-voice",
- .init = ams_delta_cx20442_init,
- .platform_name = "omap-mcbsp.1",
- .codec_name = "cx20442-codec",
- .ops = &ams_delta_ops,
- .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
-};
-
-/* Audio card driver */
-static struct snd_soc_card ams_delta_audio_card = {
- .name = "AMS_DELTA",
- .owner = THIS_MODULE,
- .dai_link = &ams_delta_dai_link,
- .num_links = 1,
-
- .controls = ams_delta_audio_controls,
- .num_controls = ARRAY_SIZE(ams_delta_audio_controls),
- .dapm_widgets = ams_delta_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ams_delta_dapm_widgets),
- .dapm_routes = ams_delta_audio_map,
- .num_dapm_routes = ARRAY_SIZE(ams_delta_audio_map),
-};
-
-/* Module init/exit */
-static int ams_delta_probe(struct platform_device *pdev)
-{
- struct snd_soc_card *card = &ams_delta_audio_card;
- int ret;
-
- card->dev = &pdev->dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- card->dev = NULL;
- return ret;
- }
- return 0;
-}
-
-static int ams_delta_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- if (tty_unregister_ldisc(N_V253) != 0)
- dev_warn(&pdev->dev,
- "failed to unregister V253 line discipline\n");
-
- snd_soc_unregister_card(card);
- card->dev = NULL;
- return 0;
-}
-
-#define DRV_NAME "ams-delta-audio"
-
-static struct platform_driver ams_delta_driver = {
- .driver = {
- .name = DRV_NAME,
- },
- .probe = ams_delta_probe,
- .remove = ams_delta_remove,
-};
-
-module_platform_driver(ams_delta_driver);
-
-MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
-MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
+++ /dev/null
-/*
- * n810.c -- SoC audio for Nokia N810
- *
- * Copyright (C) 2008 Nokia Corporation
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- *
- * 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
- *
- */
-
-#include <linux/clk.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-
-#define N810_HEADSET_AMP_GPIO 10
-#define N810_SPEAKER_AMP_GPIO 101
-
-enum {
- N810_JACK_DISABLED,
- N810_JACK_HP,
- N810_JACK_HS,
- N810_JACK_MIC,
-};
-
-static struct clk *sys_clkout2;
-static struct clk *sys_clkout2_src;
-static struct clk *func96m_clk;
-
-static int n810_spk_func;
-static int n810_jack_func;
-static int n810_dmic_func;
-
-static void n810_ext_control(struct snd_soc_dapm_context *dapm)
-{
- int hp = 0, line1l = 0;
-
- switch (n810_jack_func) {
- case N810_JACK_HS:
- line1l = 1;
- case N810_JACK_HP:
- hp = 1;
- break;
- case N810_JACK_MIC:
- line1l = 1;
- break;
- }
-
- snd_soc_dapm_mutex_lock(dapm);
-
- if (n810_spk_func)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
-
- if (hp)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- if (line1l)
- snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
-
- if (n810_dmic_func)
- snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-}
-
-static int n810_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
- snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
-
- n810_ext_control(&rtd->card->dapm);
- return clk_prepare_enable(sys_clkout2);
-}
-
-static void n810_shutdown(struct snd_pcm_substream *substream)
-{
- clk_disable_unprepare(sys_clkout2);
-}
-
-static int n810_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int err;
-
- /* Set the codec system clock for DAC and ADC */
- err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
- SND_SOC_CLOCK_IN);
-
- return err;
-}
-
-static const struct snd_soc_ops n810_ops = {
- .startup = n810_startup,
- .hw_params = n810_hw_params,
- .shutdown = n810_shutdown,
-};
-
-static int n810_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = n810_spk_func;
-
- return 0;
-}
-
-static int n810_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (n810_spk_func == ucontrol->value.enumerated.item[0])
- return 0;
-
- n810_spk_func = ucontrol->value.enumerated.item[0];
- n810_ext_control(&card->dapm);
-
- return 1;
-}
-
-static int n810_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = n810_jack_func;
-
- return 0;
-}
-
-static int n810_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (n810_jack_func == ucontrol->value.enumerated.item[0])
- return 0;
-
- n810_jack_func = ucontrol->value.enumerated.item[0];
- n810_ext_control(&card->dapm);
-
- return 1;
-}
-
-static int n810_get_input(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = n810_dmic_func;
-
- return 0;
-}
-
-static int n810_set_input(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (n810_dmic_func == ucontrol->value.enumerated.item[0])
- return 0;
-
- n810_dmic_func = ucontrol->value.enumerated.item[0];
- n810_ext_control(&card->dapm);
-
- return 1;
-}
-
-static int n810_spk_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
- else
- gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
-
- return 0;
-}
-
-static int n810_jack_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
- else
- gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
- SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
- SND_SOC_DAPM_MIC("DMic", NULL),
- SND_SOC_DAPM_MIC("HS Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Jack", NULL, "HPLOUT"},
- {"Headphone Jack", NULL, "HPROUT"},
-
- {"Ext Spk", NULL, "LLOUT"},
- {"Ext Spk", NULL, "RLOUT"},
-
- {"DMic Rate 64", NULL, "DMic"},
- {"DMic", NULL, "Mic Bias"},
-
- /*
- * Note that the mic bias is coming from Retu/Vilma and we don't have
- * control over it atm. The analog HS mic is not working. <- TODO
- */
- {"LINE1L", NULL, "HS Mic"},
-};
-
-static const char *spk_function[] = {"Off", "On"};
-static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
-static const char *input_function[] = {"ADC", "Digital Mic"};
-static const struct soc_enum n810_enum[] = {
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
-};
-
-static const struct snd_kcontrol_new aic33_n810_controls[] = {
- SOC_ENUM_EXT("Speaker Function", n810_enum[0],
- n810_get_spk, n810_set_spk),
- SOC_ENUM_EXT("Jack Function", n810_enum[1],
- n810_get_jack, n810_set_jack),
- SOC_ENUM_EXT("Input Select", n810_enum[2],
- n810_get_input, n810_set_input),
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link n810_dai = {
- .name = "TLV320AIC33",
- .stream_name = "AIC33",
- .cpu_dai_name = "48076000.mcbsp",
- .platform_name = "48076000.mcbsp",
- .codec_name = "tlv320aic3x-codec.1-0018",
- .codec_dai_name = "tlv320aic3x-hifi",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &n810_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_n810 = {
- .name = "N810",
- .owner = THIS_MODULE,
- .dai_link = &n810_dai,
- .num_links = 1,
-
- .controls = aic33_n810_controls,
- .num_controls = ARRAY_SIZE(aic33_n810_controls),
- .dapm_widgets = aic33_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static struct platform_device *n810_snd_device;
-
-static int __init n810_soc_init(void)
-{
- int err;
- struct device *dev;
-
- if (!of_have_populated_dt() ||
- (!of_machine_is_compatible("nokia,n810") &&
- !of_machine_is_compatible("nokia,n810-wimax")))
- return -ENODEV;
-
- n810_snd_device = platform_device_alloc("soc-audio", -1);
- if (!n810_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(n810_snd_device, &snd_soc_n810);
- err = platform_device_add(n810_snd_device);
- if (err)
- goto err1;
-
- dev = &n810_snd_device->dev;
-
- sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
- if (IS_ERR(sys_clkout2_src)) {
- dev_err(dev, "Could not get sys_clkout2_src clock\n");
- err = PTR_ERR(sys_clkout2_src);
- goto err2;
- }
- sys_clkout2 = clk_get(dev, "sys_clkout2");
- if (IS_ERR(sys_clkout2)) {
- dev_err(dev, "Could not get sys_clkout2\n");
- err = PTR_ERR(sys_clkout2);
- goto err3;
- }
- /*
- * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
- * 96 MHz as its parent in order to get 12 MHz
- */
- func96m_clk = clk_get(dev, "func_96m_ck");
- if (IS_ERR(func96m_clk)) {
- dev_err(dev, "Could not get func 96M clock\n");
- err = PTR_ERR(func96m_clk);
- goto err4;
- }
- clk_set_parent(sys_clkout2_src, func96m_clk);
- clk_set_rate(sys_clkout2, 12000000);
-
- if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
- (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
- err = -EINVAL;
- goto err4;
- }
-
- gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
- gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
-
- return 0;
-err4:
- clk_put(sys_clkout2);
-err3:
- clk_put(sys_clkout2_src);
-err2:
- platform_device_del(n810_snd_device);
-err1:
- platform_device_put(n810_snd_device);
-
- return err;
-}
-
-static void __exit n810_soc_exit(void)
-{
- gpio_free(N810_SPEAKER_AMP_GPIO);
- gpio_free(N810_HEADSET_AMP_GPIO);
- clk_put(sys_clkout2_src);
- clk_put(sys_clkout2);
- clk_put(func96m_clk);
-
- platform_device_unregister(n810_snd_device);
-}
-
-module_init(n810_soc_init);
-module_exit(n810_soc_exit);
-
-MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
-MODULE_DESCRIPTION("ALSA SoC Nokia N810");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * omap-abe-twl6040.c -- SoC audio for TI OMAP based boards with ABE and
- * twl6040 codec
- *
- * Author: Misael Lopez Cruz <misael.lopez@ti.com>
- *
- * 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
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/twl6040.h>
-#include <linux/module.h>
-#include <linux/of.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include "omap-dmic.h"
-#include "omap-mcpdm.h"
-#include "../codecs/twl6040.h"
-
-struct abe_twl6040 {
- struct snd_soc_card card;
- struct snd_soc_dai_link dai_links[2];
- int jack_detection; /* board can detect jack events */
- int mclk_freq; /* MCLK frequency speed for twl6040 */
-};
-
-static struct platform_device *dmic_codec_dev;
-
-static int omap_abe_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_card *card = rtd->card;
- struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
- int clk_id, freq;
- int ret;
-
- clk_id = twl6040_get_clk_id(codec_dai->component);
- if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
- freq = priv->mclk_freq;
- else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
- freq = 32768;
- else
- return -EINVAL;
-
- /* set the codec mclk */
- ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
- SND_SOC_CLOCK_IN);
- if (ret) {
- printk(KERN_ERR "can't set codec system clock\n");
- return ret;
- }
- return ret;
-}
-
-static const struct snd_soc_ops omap_abe_ops = {
- .hw_params = omap_abe_hw_params,
-};
-
-static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret = 0;
-
- ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS,
- 19200000, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- printk(KERN_ERR "can't set DMIC cpu system clock\n");
- return ret;
- }
- ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000,
- SND_SOC_CLOCK_OUT);
- if (ret < 0) {
- printk(KERN_ERR "can't set DMIC output clock\n");
- return ret;
- }
- return 0;
-}
-
-static struct snd_soc_ops omap_abe_dmic_ops = {
- .hw_params = omap_abe_dmic_hw_params,
-};
-
-/* Headset jack */
-static struct snd_soc_jack hs_jack;
-
-/*Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Headset Mic",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Headset Stereophone",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-/* SDP4430 machine DAPM */
-static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
- /* Outputs */
- SND_SOC_DAPM_HP("Headset Stereophone", NULL),
- SND_SOC_DAPM_SPK("Earphone Spk", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
- SND_SOC_DAPM_SPK("Vibrator", NULL),
-
- /* Inputs */
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
- SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-
- /* Digital microphones */
- SND_SOC_DAPM_MIC("Digital Mic", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Routings for outputs */
- {"Headset Stereophone", NULL, "HSOL"},
- {"Headset Stereophone", NULL, "HSOR"},
-
- {"Earphone Spk", NULL, "EP"},
-
- {"Ext Spk", NULL, "HFL"},
- {"Ext Spk", NULL, "HFR"},
-
- {"Line Out", NULL, "AUXL"},
- {"Line Out", NULL, "AUXR"},
-
- {"Vibrator", NULL, "VIBRAL"},
- {"Vibrator", NULL, "VIBRAR"},
-
- /* Routings for inputs */
- {"HSMIC", NULL, "Headset Mic"},
- {"Headset Mic", NULL, "Headset Mic Bias"},
-
- {"MAINMIC", NULL, "Main Handset Mic"},
- {"Main Handset Mic", NULL, "Main Mic Bias"},
-
- {"SUBMIC", NULL, "Sub Handset Mic"},
- {"Sub Handset Mic", NULL, "Main Mic Bias"},
-
- {"AFML", NULL, "Line In"},
- {"AFMR", NULL, "Line In"},
-};
-
-static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_component *component = rtd->codec_dai->component;
- struct snd_soc_card *card = rtd->card;
- struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
- int hs_trim;
- int ret = 0;
-
- /*
- * Configure McPDM offset cancellation based on the HSOTRIM value from
- * twl6040.
- */
- hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM);
- omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim),
- TWL6040_HSF_TRIM_RIGHT(hs_trim));
-
- /* Headset jack detection only if it is supported */
- if (priv->jack_detection) {
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET, &hs_jack,
- hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
- if (ret)
- return ret;
-
- twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET);
- }
-
- return 0;
-}
-
-static const struct snd_soc_dapm_route dmic_audio_map[] = {
- {"DMic", NULL, "Digital Mic"},
- {"Digital Mic", NULL, "Digital Mic1 Bias"},
-};
-
-static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
-
- return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
- ARRAY_SIZE(dmic_audio_map));
-}
-
-static int omap_abe_probe(struct platform_device *pdev)
-{
- struct device_node *node = pdev->dev.of_node;
- struct snd_soc_card *card;
- struct device_node *dai_node;
- struct abe_twl6040 *priv;
- int num_links = 0;
- int ret = 0;
-
- if (!node) {
- dev_err(&pdev->dev, "of node is missing.\n");
- return -ENODEV;
- }
-
- priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
-
- card = &priv->card;
- card->dev = &pdev->dev;
- card->owner = THIS_MODULE;
- card->dapm_widgets = twl6040_dapm_widgets;
- card->num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets);
- card->dapm_routes = audio_map;
- card->num_dapm_routes = ARRAY_SIZE(audio_map);
-
- if (snd_soc_of_parse_card_name(card, "ti,model")) {
- dev_err(&pdev->dev, "Card name is not provided\n");
- return -ENODEV;
- }
-
- ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
- if (ret) {
- dev_err(&pdev->dev, "Error while parsing DAPM routing\n");
- return ret;
- }
-
- dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
- if (!dai_node) {
- dev_err(&pdev->dev, "McPDM node is not provided\n");
- return -EINVAL;
- }
-
- priv->dai_links[0].name = "DMIC";
- priv->dai_links[0].stream_name = "TWL6040";
- priv->dai_links[0].cpu_of_node = dai_node;
- priv->dai_links[0].platform_of_node = dai_node;
- priv->dai_links[0].codec_dai_name = "twl6040-legacy";
- priv->dai_links[0].codec_name = "twl6040-codec";
- priv->dai_links[0].init = omap_abe_twl6040_init;
- priv->dai_links[0].ops = &omap_abe_ops;
-
- dai_node = of_parse_phandle(node, "ti,dmic", 0);
- if (dai_node) {
- num_links = 2;
- priv->dai_links[1].name = "TWL6040";
- priv->dai_links[1].stream_name = "DMIC Capture";
- priv->dai_links[1].cpu_of_node = dai_node;
- priv->dai_links[1].platform_of_node = dai_node;
- priv->dai_links[1].codec_dai_name = "dmic-hifi";
- priv->dai_links[1].codec_name = "dmic-codec";
- priv->dai_links[1].init = omap_abe_dmic_init;
- priv->dai_links[1].ops = &omap_abe_dmic_ops;
- } else {
- num_links = 1;
- }
-
- priv->jack_detection = of_property_read_bool(node, "ti,jack-detection");
- of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq);
- if (!priv->mclk_freq) {
- dev_err(&pdev->dev, "MCLK frequency not provided\n");
- return -EINVAL;
- }
-
- card->fully_routed = 1;
-
- if (!priv->mclk_freq) {
- dev_err(&pdev->dev, "MCLK frequency missing\n");
- return -ENODEV;
- }
-
- card->dai_link = priv->dai_links;
- card->num_links = num_links;
-
- snd_soc_card_set_drvdata(card, priv);
-
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
- ret);
-
- return ret;
-}
-
-static const struct of_device_id omap_abe_of_match[] = {
- {.compatible = "ti,abe-twl6040", },
- { },
-};
-MODULE_DEVICE_TABLE(of, omap_abe_of_match);
-
-static struct platform_driver omap_abe_driver = {
- .driver = {
- .name = "omap-abe-twl6040",
- .pm = &snd_soc_pm_ops,
- .of_match_table = omap_abe_of_match,
- },
- .probe = omap_abe_probe,
-};
-
-static int __init omap_abe_init(void)
-{
- int ret;
-
- dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL,
- 0);
- if (IS_ERR(dmic_codec_dev)) {
- pr_err("%s: dmic-codec device registration failed\n", __func__);
- return PTR_ERR(dmic_codec_dev);
- }
-
- ret = platform_driver_register(&omap_abe_driver);
- if (ret) {
- pr_err("%s: platform driver registration failed\n", __func__);
- platform_device_unregister(dmic_codec_dev);
- }
-
- return ret;
-}
-module_init(omap_abe_init);
-
-static void __exit omap_abe_exit(void)
-{
- platform_driver_unregister(&omap_abe_driver);
- platform_device_unregister(dmic_codec_dev);
-}
-module_exit(omap_abe_exit);
-
-MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:omap-abe-twl6040");
+++ /dev/null
-/*
- * omap-dmic.c -- OMAP ASoC DMIC DAI driver
- *
- * Copyright (C) 2010 - 2011 Texas Instruments
- *
- * Author: David Lambert <dlambert@ti.com>
- * Misael Lopez Cruz <misael.lopez@ti.com>
- * Liam Girdwood <lrg@ti.com>
- * Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * 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
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-#include <linux/of_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "omap-dmic.h"
-#include "sdma-pcm.h"
-
-struct omap_dmic {
- struct device *dev;
- void __iomem *io_base;
- struct clk *fclk;
- struct pm_qos_request pm_qos_req;
- int latency;
- int fclk_freq;
- int out_freq;
- int clk_div;
- int sysclk;
- int threshold;
- u32 ch_enabled;
- bool active;
- struct mutex mutex;
-
- struct snd_dmaengine_dai_dma_data dma_data;
-};
-
-static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
-{
- writel_relaxed(val, dmic->io_base + reg);
-}
-
-static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
-{
- return readl_relaxed(dmic->io_base + reg);
-}
-
-static inline void omap_dmic_start(struct omap_dmic *dmic)
-{
- u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
-
- /* Configure DMA controller */
- omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG,
- OMAP_DMIC_DMA_ENABLE);
-
- omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled);
-}
-
-static inline void omap_dmic_stop(struct omap_dmic *dmic)
-{
- u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
- omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
- ctrl & ~OMAP_DMIC_UP_ENABLE_MASK);
-
- /* Disable DMA request generation */
- omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG,
- OMAP_DMIC_DMA_ENABLE);
-
-}
-
-static inline int dmic_is_enabled(struct omap_dmic *dmic)
-{
- return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) &
- OMAP_DMIC_UP_ENABLE_MASK;
-}
-
-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);
- int ret = 0;
-
- mutex_lock(&dmic->mutex);
-
- if (!dai->active)
- dmic->active = 1;
- else
- ret = -EBUSY;
-
- mutex_unlock(&dmic->mutex);
-
- return ret;
-}
-
-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);
-
- mutex_lock(&dmic->mutex);
-
- pm_qos_remove_request(&dmic->pm_qos_req);
-
- if (!dai->active)
- dmic->active = 0;
-
- mutex_unlock(&dmic->mutex);
-}
-
-static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate)
-{
- int divider = -EINVAL;
-
- /*
- * 192KHz rate is only supported with 19.2MHz/3.84MHz clock
- * configuration.
- */
- if (sample_rate == 192000) {
- if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000)
- divider = 0x6; /* Divider: 5 (192KHz sampling rate) */
- else
- dev_err(dmic->dev,
- "invalid clock configuration for 192KHz\n");
-
- return divider;
- }
-
- switch (dmic->out_freq) {
- case 1536000:
- if (dmic->fclk_freq != 24576000)
- goto div_err;
- divider = 0x4; /* Divider: 16 */
- break;
- case 2400000:
- switch (dmic->fclk_freq) {
- case 12000000:
- divider = 0x5; /* Divider: 5 */
- break;
- case 19200000:
- divider = 0x0; /* Divider: 8 */
- break;
- case 24000000:
- divider = 0x2; /* Divider: 10 */
- break;
- default:
- goto div_err;
- }
- break;
- case 3072000:
- if (dmic->fclk_freq != 24576000)
- goto div_err;
- divider = 0x3; /* Divider: 8 */
- break;
- case 3840000:
- if (dmic->fclk_freq != 19200000)
- goto div_err;
- divider = 0x1; /* Divider: 5 (96KHz sampling rate) */
- break;
- default:
- dev_err(dmic->dev, "invalid out frequency: %dHz\n",
- dmic->out_freq);
- break;
- }
-
- return divider;
-
-div_err:
- dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n",
- dmic->out_freq, dmic->fclk_freq);
- return -EINVAL;
-}
-
-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 snd_dmaengine_dai_dma_data *dma_data;
- int channels;
-
- dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
- if (dmic->clk_div < 0) {
- dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n",
- dmic->out_freq, dmic->fclk_freq);
- return -EINVAL;
- }
-
- dmic->ch_enabled = 0;
- channels = params_channels(params);
- switch (channels) {
- case 6:
- dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
- /* fall through */
- case 4:
- dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE;
- /* fall through */
- case 2:
- dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE;
- break;
- default:
- dev_err(dmic->dev, "invalid number of legacy channels\n");
- return -EINVAL;
- }
-
- /* packet size is threshold * channels */
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
- dma_data->maxburst = dmic->threshold * channels;
- dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC /
- params_rate(params);
-
- return 0;
-}
-
-static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
- u32 ctrl;
-
- if (pm_qos_request_active(&dmic->pm_qos_req))
- pm_qos_update_request(&dmic->pm_qos_req, dmic->latency);
-
- /* Configure uplink threshold */
- omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
-
- ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
-
- /* Set dmic out format */
- ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK);
- ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
- OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
-
- /* Configure dmic clock divider */
- ctrl &= ~OMAP_DMIC_CLK_DIV_MASK;
- ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div);
-
- omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl);
-
- omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
- ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
- OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
-
- return 0;
-}
-
-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);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- omap_dmic_start(dmic);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- omap_dmic_stop(dmic);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id,
- unsigned int freq)
-{
- struct clk *parent_clk, *mux;
- char *parent_clk_name;
- int ret = 0;
-
- switch (freq) {
- case 12000000:
- case 19200000:
- case 24000000:
- case 24576000:
- break;
- default:
- dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq);
- dmic->fclk_freq = 0;
- return -EINVAL;
- }
-
- if (dmic->sysclk == clk_id) {
- dmic->fclk_freq = freq;
- return 0;
- }
-
- /* re-parent not allowed if a stream is ongoing */
- if (dmic->active && dmic_is_enabled(dmic)) {
- dev_err(dmic->dev, "can't re-parent when DMIC active\n");
- return -EBUSY;
- }
-
- switch (clk_id) {
- case OMAP_DMIC_SYSCLK_PAD_CLKS:
- parent_clk_name = "pad_clks_ck";
- break;
- case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS:
- parent_clk_name = "slimbus_clk";
- break;
- case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS:
- parent_clk_name = "dmic_sync_mux_ck";
- break;
- default:
- dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id);
- return -EINVAL;
- }
-
- parent_clk = clk_get(dmic->dev, parent_clk_name);
- if (IS_ERR(parent_clk)) {
- dev_err(dmic->dev, "can't get %s\n", parent_clk_name);
- return -ENODEV;
- }
-
- mux = clk_get_parent(dmic->fclk);
- if (IS_ERR(mux)) {
- dev_err(dmic->dev, "can't get fck mux parent\n");
- clk_put(parent_clk);
- return -ENODEV;
- }
-
- mutex_lock(&dmic->mutex);
- if (dmic->active) {
- /* disable clock while reparenting */
- pm_runtime_put_sync(dmic->dev);
- ret = clk_set_parent(mux, parent_clk);
- pm_runtime_get_sync(dmic->dev);
- } else {
- ret = clk_set_parent(mux, parent_clk);
- }
- mutex_unlock(&dmic->mutex);
-
- if (ret < 0) {
- dev_err(dmic->dev, "re-parent failed\n");
- goto err_busy;
- }
-
- dmic->sysclk = clk_id;
- dmic->fclk_freq = freq;
-
-err_busy:
- clk_put(mux);
- clk_put(parent_clk);
-
- return ret;
-}
-
-static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id,
- unsigned int freq)
-{
- int ret = 0;
-
- if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) {
- dev_err(dmic->dev, "output clk_id (%d) not supported\n",
- clk_id);
- return -EINVAL;
- }
-
- switch (freq) {
- case 1536000:
- case 2400000:
- case 3072000:
- case 3840000:
- dmic->out_freq = freq;
- break;
- default:
- dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq);
- dmic->out_freq = 0;
- ret = -EINVAL;
- }
-
- return ret;
-}
-
-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);
-
- if (dir == SND_SOC_CLOCK_IN)
- return omap_dmic_select_fclk(dmic, clk_id, freq);
- else if (dir == SND_SOC_CLOCK_OUT)
- return omap_dmic_select_outclk(dmic, clk_id, freq);
-
- dev_err(dmic->dev, "invalid clock direction (%d)\n", dir);
- return -EINVAL;
-}
-
-static const 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,
- .prepare = omap_dmic_dai_prepare,
- .trigger = omap_dmic_dai_trigger,
- .set_sysclk = omap_dmic_set_dai_sysclk,
-};
-
-static int omap_dmic_probe(struct snd_soc_dai *dai)
-{
- struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_enable(dmic->dev);
-
- /* Disable lines while request is ongoing */
- pm_runtime_get_sync(dmic->dev);
- omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
- pm_runtime_put_sync(dmic->dev);
-
- /* Configure DMIC threshold value */
- dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
-
- snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data);
-
- return 0;
-}
-
-static int omap_dmic_remove(struct snd_soc_dai *dai)
-{
- struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_disable(dmic->dev);
-
- return 0;
-}
-
-static struct snd_soc_dai_driver omap_dmic_dai = {
- .name = "omap-dmic",
- .probe = omap_dmic_probe,
- .remove = omap_dmic_remove,
- .capture = {
- .channels_min = 2,
- .channels_max = 6,
- .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
- .sig_bits = 24,
- },
- .ops = &omap_dmic_dai_ops,
-};
-
-static const struct snd_soc_component_driver omap_dmic_component = {
- .name = "omap-dmic",
-};
-
-static int asoc_dmic_probe(struct platform_device *pdev)
-{
- struct omap_dmic *dmic;
- struct resource *res;
- int ret;
-
- dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL);
- if (!dmic)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, dmic);
- dmic->dev = &pdev->dev;
- dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS;
-
- mutex_init(&dmic->mutex);
-
- dmic->fclk = devm_clk_get(dmic->dev, "fck");
- if (IS_ERR(dmic->fclk)) {
- dev_err(dmic->dev, "cant get fck\n");
- return -ENODEV;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
- if (!res) {
- dev_err(dmic->dev, "invalid dma memory resource\n");
- return -ENODEV;
- }
- dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG;
-
- dmic->dma_data.filter_data = "up_link";
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(dmic->io_base))
- return PTR_ERR(dmic->io_base);
-
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &omap_dmic_component,
- &omap_dmic_dai, 1);
- if (ret)
- return ret;
-
- ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link");
- if (ret)
- return ret;
-
- return 0;
-}
-
-static const struct of_device_id omap_dmic_of_match[] = {
- { .compatible = "ti,omap4-dmic", },
- { }
-};
-MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
-
-static struct platform_driver asoc_dmic_driver = {
- .driver = {
- .name = "omap-dmic",
- .of_match_table = omap_dmic_of_match,
- },
- .probe = asoc_dmic_probe,
-};
-
-module_platform_driver(asoc_dmic_driver);
-
-MODULE_ALIAS("platform:omap-dmic");
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_DESCRIPTION("OMAP DMIC ASoC Interface");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * omap-dmic.h -- OMAP Digital Microphone Controller
- *
- * 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
-
-#define OMAP_DMIC_REVISION_REG 0x00
-#define OMAP_DMIC_SYSCONFIG_REG 0x10
-#define OMAP_DMIC_IRQSTATUS_RAW_REG 0x24
-#define OMAP_DMIC_IRQSTATUS_REG 0x28
-#define OMAP_DMIC_IRQENABLE_SET_REG 0x2C
-#define OMAP_DMIC_IRQENABLE_CLR_REG 0x30
-#define OMAP_DMIC_IRQWAKE_EN_REG 0x34
-#define OMAP_DMIC_DMAENABLE_SET_REG 0x38
-#define OMAP_DMIC_DMAENABLE_CLR_REG 0x3C
-#define OMAP_DMIC_DMAWAKEEN_REG 0x40
-#define OMAP_DMIC_CTRL_REG 0x44
-#define OMAP_DMIC_DATA_REG 0x48
-#define OMAP_DMIC_FIFO_CTRL_REG 0x4C
-#define OMAP_DMIC_FIFO_DMIC1R_DATA_REG 0x50
-#define OMAP_DMIC_FIFO_DMIC1L_DATA_REG 0x54
-#define OMAP_DMIC_FIFO_DMIC2R_DATA_REG 0x58
-#define OMAP_DMIC_FIFO_DMIC2L_DATA_REG 0x5C
-#define OMAP_DMIC_FIFO_DMIC3R_DATA_REG 0x60
-#define OMAP_DMIC_FIFO_DMIC3L_DATA_REG 0x64
-
-/* IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR bit fields */
-#define OMAP_DMIC_IRQ (1 << 0)
-#define OMAP_DMIC_IRQ_FULL (1 << 1)
-#define OMAP_DMIC_IRQ_ALMST_EMPTY (1 << 2)
-#define OMAP_DMIC_IRQ_EMPTY (1 << 3)
-#define OMAP_DMIC_IRQ_MASK 0x07
-
-/* DMIC_DMAENABLE bit fields */
-#define OMAP_DMIC_DMA_ENABLE 0x1
-
-/* DMIC_CTRL bit fields */
-#define OMAP_DMIC_UP1_ENABLE (1 << 0)
-#define OMAP_DMIC_UP2_ENABLE (1 << 1)
-#define OMAP_DMIC_UP3_ENABLE (1 << 2)
-#define OMAP_DMIC_UP_ENABLE_MASK 0x7
-#define OMAP_DMIC_FORMAT (1 << 3)
-#define OMAP_DMIC_POLAR1 (1 << 4)
-#define OMAP_DMIC_POLAR2 (1 << 5)
-#define OMAP_DMIC_POLAR3 (1 << 6)
-#define OMAP_DMIC_POLAR_MASK (0x7 << 4)
-#define OMAP_DMIC_CLK_DIV(x) (((x) & 0x7) << 7)
-#define OMAP_DMIC_CLK_DIV_MASK (0x7 << 7)
-#define OMAP_DMIC_RESET (1 << 10)
-
-#define OMAP_DMICOUTFORMAT_LJUST (0 << 3)
-#define OMAP_DMICOUTFORMAT_RJUST (1 << 3)
-
-/* DMIC_FIFO_CTRL bit fields */
-#define OMAP_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 */
- OMAP_DMIC_ABE_DMIC_CLK, /* abe_dmic_clk */
-};
-
-#endif
+++ /dev/null
-/*
- * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library
- *
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
- *
- * Author: Jyri Sarha <jsarha@ti.com>
- *
- * 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.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/string.h>
-#include <linux/platform_device.h>
-#include <sound/soc.h>
-#include <sound/pcm_params.h>
-#include <sound/dmaengine_pcm.h>
-#include <uapi/sound/asound.h>
-#include <sound/asoundef.h>
-#include <sound/omap-hdmi-audio.h>
-
-#include "sdma-pcm.h"
-
-#define DRV_NAME "omap-hdmi-audio"
-
-struct hdmi_audio_data {
- struct snd_soc_card *card;
-
- const struct omap_hdmi_audio_ops *ops;
- struct device *dssdev;
- struct snd_dmaengine_dai_dma_data dma_data;
- struct omap_dss_audio dss_audio;
- struct snd_aes_iec958 iec;
- struct snd_cea_861_aud_if cea;
-
- struct mutex current_stream_lock;
- struct snd_pcm_substream *current_stream;
-};
-
-static
-struct hdmi_audio_data *card_drvdata_substream(struct snd_pcm_substream *ss)
-{
- struct snd_soc_pcm_runtime *rtd = ss->private_data;
-
- return snd_soc_card_get_drvdata(rtd->card);
-}
-
-static void hdmi_dai_abort(struct device *dev)
-{
- struct hdmi_audio_data *ad = dev_get_drvdata(dev);
-
- mutex_lock(&ad->current_stream_lock);
- if (ad->current_stream && ad->current_stream->runtime &&
- snd_pcm_running(ad->current_stream)) {
- dev_err(dev, "HDMI display disabled, aborting playback\n");
- snd_pcm_stream_lock_irq(ad->current_stream);
- snd_pcm_stop(ad->current_stream, SNDRV_PCM_STATE_DISCONNECTED);
- snd_pcm_stream_unlock_irq(ad->current_stream);
- }
- mutex_unlock(&ad->current_stream_lock);
-}
-
-static int hdmi_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdmi_audio_data *ad = card_drvdata_substream(substream);
- int ret;
- /*
- * Make sure that the period bytes are multiple of the DMA packet size.
- * Largest packet size we use is 32 32-bit words = 128 bytes
- */
- ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
- if (ret < 0) {
- dev_err(dai->dev, "Could not apply period constraint: %d\n",
- ret);
- return ret;
- }
- ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
- if (ret < 0) {
- dev_err(dai->dev, "Could not apply buffer constraint: %d\n",
- ret);
- return ret;
- }
-
- snd_soc_dai_set_dma_data(dai, substream, &ad->dma_data);
-
- mutex_lock(&ad->current_stream_lock);
- ad->current_stream = substream;
- mutex_unlock(&ad->current_stream_lock);
-
- ret = ad->ops->audio_startup(ad->dssdev, hdmi_dai_abort);
-
- if (ret) {
- mutex_lock(&ad->current_stream_lock);
- ad->current_stream = NULL;
- mutex_unlock(&ad->current_stream_lock);
- }
-
- return ret;
-}
-
-static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct hdmi_audio_data *ad = card_drvdata_substream(substream);
- struct snd_aes_iec958 *iec = &ad->iec;
- struct snd_cea_861_aud_if *cea = &ad->cea;
-
- WARN_ON(ad->current_stream != substream);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- ad->dma_data.maxburst = 16;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- ad->dma_data.maxburst = 32;
- break;
- default:
- dev_err(dai->dev, "format not supported!\n");
- return -EINVAL;
- }
-
- ad->dss_audio.iec = iec;
- ad->dss_audio.cea = cea;
- /*
- * fill the IEC-60958 channel status word
- */
- /* initialize the word bytes */
- memset(iec->status, 0, sizeof(iec->status));
-
- /* specify IEC-60958-3 (commercial use) */
- iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
-
- /* specify that the audio is LPCM*/
- iec->status[0] &= ~IEC958_AES0_NONAUDIO;
-
- iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
-
- iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
-
- iec->status[1] = IEC958_AES1_CON_GENERAL;
-
- iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
-
- iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC;
-
- switch (params_rate(params)) {
- case 32000:
- iec->status[3] |= IEC958_AES3_CON_FS_32000;
- break;
- case 44100:
- iec->status[3] |= IEC958_AES3_CON_FS_44100;
- break;
- case 48000:
- iec->status[3] |= IEC958_AES3_CON_FS_48000;
- break;
- case 88200:
- iec->status[3] |= IEC958_AES3_CON_FS_88200;
- break;
- case 96000:
- iec->status[3] |= IEC958_AES3_CON_FS_96000;
- break;
- case 176400:
- iec->status[3] |= IEC958_AES3_CON_FS_176400;
- break;
- case 192000:
- iec->status[3] |= IEC958_AES3_CON_FS_192000;
- break;
- default:
- dev_err(dai->dev, "rate not supported!\n");
- return -EINVAL;
- }
-
- /* specify the clock accuracy */
- iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM;
-
- /*
- * specify the word length. The same word length value can mean
- * two different lengths. Hence, we need to specify the maximum
- * word length as well.
- */
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16;
- iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24;
- break;
- case SNDRV_PCM_FORMAT_S24_LE:
- iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20;
- iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24;
- break;
- default:
- dev_err(dai->dev, "format not supported!\n");
- return -EINVAL;
- }
-
- /*
- * Fill the CEA-861 audio infoframe (see spec for details)
- */
-
- cea->db1_ct_cc = (params_channels(params) - 1)
- & CEA861_AUDIO_INFOFRAME_DB1CC;
- cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM;
-
- cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM;
- cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM;
-
- cea->db3 = 0; /* not used, all zeros */
-
- if (params_channels(params) == 2)
- cea->db4_ca = 0x0;
- else if (params_channels(params) == 6)
- cea->db4_ca = 0xb;
- else
- cea->db4_ca = 0x13;
-
- if (cea->db4_ca == 0x00)
- cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED;
- else
- cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
-
- /* the expression is trivial but makes clear what we are doing */
- cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
-
- return ad->ops->audio_config(ad->dssdev, &ad->dss_audio);
-}
-
-static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
-{
- struct hdmi_audio_data *ad = card_drvdata_substream(substream);
- int err = 0;
-
- WARN_ON(ad->current_stream != substream);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- err = ad->ops->audio_start(ad->dssdev);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ad->ops->audio_stop(ad->dssdev);
- break;
- default:
- err = -EINVAL;
- }
- return err;
-}
-
-static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct hdmi_audio_data *ad = card_drvdata_substream(substream);
-
- WARN_ON(ad->current_stream != substream);
-
- ad->ops->audio_shutdown(ad->dssdev);
-
- mutex_lock(&ad->current_stream_lock);
- ad->current_stream = NULL;
- mutex_unlock(&ad->current_stream_lock);
-}
-
-static const struct snd_soc_dai_ops hdmi_dai_ops = {
- .startup = hdmi_dai_startup,
- .hw_params = hdmi_dai_hw_params,
- .trigger = hdmi_dai_trigger,
- .shutdown = hdmi_dai_shutdown,
-};
-
-static const struct snd_soc_component_driver omap_hdmi_component = {
- .name = "omapdss_hdmi",
-};
-
-static struct snd_soc_dai_driver omap5_hdmi_dai = {
- .name = "omap5-hdmi-dai",
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
- SNDRV_PCM_RATE_192000),
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &hdmi_dai_ops,
-};
-
-static struct snd_soc_dai_driver omap4_hdmi_dai = {
- .name = "omap4-hdmi-dai",
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
- SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
- SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
- SNDRV_PCM_RATE_192000),
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
- },
- .ops = &hdmi_dai_ops,
-};
-
-static int omap_hdmi_audio_probe(struct platform_device *pdev)
-{
- struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data;
- struct device *dev = &pdev->dev;
- struct hdmi_audio_data *ad;
- struct snd_soc_dai_driver *dai_drv;
- struct snd_soc_card *card;
- int ret;
-
- if (!ha) {
- dev_err(dev, "No platform data\n");
- return -EINVAL;
- }
-
- ad = devm_kzalloc(dev, sizeof(*ad), GFP_KERNEL);
- if (!ad)
- return -ENOMEM;
- ad->dssdev = ha->dev;
- ad->ops = ha->ops;
- ad->dma_data.addr = ha->audio_dma_addr;
- ad->dma_data.filter_data = "audio_tx";
- ad->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- mutex_init(&ad->current_stream_lock);
-
- switch (ha->version) {
- case 4:
- dai_drv = &omap4_hdmi_dai;
- break;
- case 5:
- dai_drv = &omap5_hdmi_dai;
- break;
- default:
- return -EINVAL;
- }
- ret = devm_snd_soc_register_component(ad->dssdev, &omap_hdmi_component,
- dai_drv, 1);
- if (ret)
- return ret;
-
- ret = sdma_pcm_platform_register(ad->dssdev, "audio_tx", NULL);
- if (ret)
- return ret;
-
- card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
- if (!card)
- return -ENOMEM;
-
- card->name = devm_kasprintf(dev, GFP_KERNEL,
- "HDMI %s", dev_name(ad->dssdev));
- if (!card->name)
- return -ENOMEM;
-
- card->owner = THIS_MODULE;
- card->dai_link =
- devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL);
- if (!card->dai_link)
- return -ENOMEM;
- card->dai_link->name = card->name;
- card->dai_link->stream_name = card->name;
- card->dai_link->cpu_dai_name = dev_name(ad->dssdev);
- card->dai_link->platform_name = dev_name(ad->dssdev);
- card->dai_link->codec_name = "snd-soc-dummy";
- card->dai_link->codec_dai_name = "snd-soc-dummy-dai";
- card->num_links = 1;
- card->dev = dev;
-
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(dev, "snd_soc_register_card failed (%d)\n", ret);
- return ret;
- }
-
- ad->card = card;
- snd_soc_card_set_drvdata(card, ad);
-
- dev_set_drvdata(dev, ad);
-
- return 0;
-}
-
-static int omap_hdmi_audio_remove(struct platform_device *pdev)
-{
- struct hdmi_audio_data *ad = platform_get_drvdata(pdev);
-
- snd_soc_unregister_card(ad->card);
- return 0;
-}
-
-static struct platform_driver hdmi_audio_driver = {
- .driver = {
- .name = DRV_NAME,
- },
- .probe = omap_hdmi_audio_probe,
- .remove = omap_hdmi_audio_remove,
-};
-
-module_platform_driver(hdmi_audio_driver);
-
-MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
-MODULE_DESCRIPTION("OMAP HDMI Audio Driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * OMAP Multi-Channel Buffered Serial Port
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * Peter Ujfalusi <peter.ujfalusi@ti.com>
- */
-
-#ifndef __OMAP_MCBSP_PRIV_H__
-#define __OMAP_MCBSP_PRIV_H__
-
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#ifdef CONFIG_ARCH_OMAP1
-#define mcbsp_omap1() 1
-#else
-#define mcbsp_omap1() 0
-#endif
-
-/* McBSP register numbers. Register address offset = num * reg_step */
-enum {
- /* Common registers */
- OMAP_MCBSP_REG_SPCR2 = 4,
- OMAP_MCBSP_REG_SPCR1,
- OMAP_MCBSP_REG_RCR2,
- OMAP_MCBSP_REG_RCR1,
- OMAP_MCBSP_REG_XCR2,
- OMAP_MCBSP_REG_XCR1,
- OMAP_MCBSP_REG_SRGR2,
- OMAP_MCBSP_REG_SRGR1,
- OMAP_MCBSP_REG_MCR2,
- OMAP_MCBSP_REG_MCR1,
- OMAP_MCBSP_REG_RCERA,
- OMAP_MCBSP_REG_RCERB,
- OMAP_MCBSP_REG_XCERA,
- OMAP_MCBSP_REG_XCERB,
- OMAP_MCBSP_REG_PCR0,
- OMAP_MCBSP_REG_RCERC,
- OMAP_MCBSP_REG_RCERD,
- OMAP_MCBSP_REG_XCERC,
- OMAP_MCBSP_REG_XCERD,
- OMAP_MCBSP_REG_RCERE,
- OMAP_MCBSP_REG_RCERF,
- OMAP_MCBSP_REG_XCERE,
- OMAP_MCBSP_REG_XCERF,
- OMAP_MCBSP_REG_RCERG,
- OMAP_MCBSP_REG_RCERH,
- OMAP_MCBSP_REG_XCERG,
- OMAP_MCBSP_REG_XCERH,
-
- /* OMAP1-OMAP2420 registers */
- OMAP_MCBSP_REG_DRR2 = 0,
- OMAP_MCBSP_REG_DRR1,
- OMAP_MCBSP_REG_DXR2,
- OMAP_MCBSP_REG_DXR1,
-
- /* OMAP2430 and onwards */
- OMAP_MCBSP_REG_DRR = 0,
- OMAP_MCBSP_REG_DXR = 2,
- OMAP_MCBSP_REG_SYSCON = 35,
- OMAP_MCBSP_REG_THRSH2,
- OMAP_MCBSP_REG_THRSH1,
- OMAP_MCBSP_REG_IRQST = 40,
- OMAP_MCBSP_REG_IRQEN,
- OMAP_MCBSP_REG_WAKEUPEN,
- OMAP_MCBSP_REG_XCCR,
- OMAP_MCBSP_REG_RCCR,
- OMAP_MCBSP_REG_XBUFFSTAT,
- OMAP_MCBSP_REG_RBUFFSTAT,
- OMAP_MCBSP_REG_SSELCR,
-};
-
-/************************** McBSP SPCR1 bit definitions ***********************/
-#define RRST BIT(0)
-#define RRDY BIT(1)
-#define RFULL BIT(2)
-#define RSYNC_ERR BIT(3)
-#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */
-#define ABIS BIT(6)
-#define DXENA BIT(7)
-#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */
-#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */
-#define ALB BIT(15)
-#define DLB BIT(15)
-
-/************************** McBSP SPCR2 bit definitions ***********************/
-#define XRST BIT(0)
-#define XRDY BIT(1)
-#define XEMPTY BIT(2)
-#define XSYNC_ERR BIT(3)
-#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */
-#define GRST BIT(6)
-#define FRST BIT(7)
-#define SOFT BIT(8)
-#define FREE BIT(9)
-
-/************************** McBSP PCR bit definitions *************************/
-#define CLKRP BIT(0)
-#define CLKXP BIT(1)
-#define FSRP BIT(2)
-#define FSXP BIT(3)
-#define DR_STAT BIT(4)
-#define DX_STAT BIT(5)
-#define CLKS_STAT BIT(6)
-#define SCLKME BIT(7)
-#define CLKRM BIT(8)
-#define CLKXM BIT(9)
-#define FSRM BIT(10)
-#define FSXM BIT(11)
-#define RIOEN BIT(12)
-#define XIOEN BIT(13)
-#define IDLE_EN BIT(14)
-
-/************************** McBSP RCR1 bit definitions ************************/
-#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */
-#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
-
-/************************** McBSP XCR1 bit definitions ************************/
-#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */
-#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
-
-/*************************** McBSP RCR2 bit definitions ***********************/
-#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */
-#define RFIG BIT(2)
-#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */
-#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */
-#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
-#define RPHASE BIT(15)
-
-/*************************** McBSP XCR2 bit definitions ***********************/
-#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */
-#define XFIG BIT(2)
-#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */
-#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */
-#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
-#define XPHASE BIT(15)
-
-/************************* McBSP SRGR1 bit definitions ************************/
-#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */
-#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */
-
-/************************* McBSP SRGR2 bit definitions ************************/
-#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */
-#define FSGM BIT(12)
-#define CLKSM BIT(13)
-#define CLKSP BIT(14)
-#define GSYNC BIT(15)
-
-/************************* McBSP MCR1 bit definitions *************************/
-#define RMCM BIT(0)
-#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */
-#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */
-#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */
-
-/************************* McBSP MCR2 bit definitions *************************/
-#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */
-#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */
-#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */
-#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */
-
-/*********************** McBSP XCCR bit definitions *************************/
-#define XDISABLE BIT(0)
-#define XDMAEN BIT(3)
-#define DILB BIT(5)
-#define XFULL_CYCLE BIT(11)
-#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */
-#define PPCONNECT BIT(14)
-#define EXTCLKGATE BIT(15)
-
-/********************** McBSP RCCR bit definitions *************************/
-#define RDISABLE BIT(0)
-#define RDMAEN BIT(3)
-#define RFULL_CYCLE BIT(11)
-
-/********************** McBSP SYSCONFIG bit definitions ********************/
-#define SOFTRST BIT(1)
-#define ENAWAKEUP BIT(2)
-#define SIDLEMODE(value) (((value) & 0x3) << 3)
-#define CLOCKACTIVITY(value) (((value) & 0x3) << 8)
-
-/********************** McBSP DMA operating modes **************************/
-#define MCBSP_DMA_MODE_ELEMENT 0
-#define MCBSP_DMA_MODE_THRESHOLD 1
-
-/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/
-#define RSYNCERREN BIT(0)
-#define RFSREN BIT(1)
-#define REOFEN BIT(2)
-#define RRDYEN BIT(3)
-#define RUNDFLEN BIT(4)
-#define ROVFLEN BIT(5)
-#define XSYNCERREN BIT(7)
-#define XFSXEN BIT(8)
-#define XEOFEN BIT(9)
-#define XRDYEN BIT(10)
-#define XUNDFLEN BIT(11)
-#define XOVFLEN BIT(12)
-#define XEMPTYEOFEN BIT(14)
-
-/* Clock signal muxing options */
-#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */
-#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */
-#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */
-#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */
-
-/* McBSP functional clock sources */
-#define MCBSP_CLKS_PRCM_SRC 0
-#define MCBSP_CLKS_PAD_SRC 1
-
-/* we don't do multichannel for now */
-struct omap_mcbsp_reg_cfg {
- u16 spcr2;
- u16 spcr1;
- u16 rcr2;
- u16 rcr1;
- u16 xcr2;
- u16 xcr1;
- u16 srgr2;
- u16 srgr1;
- u16 mcr2;
- u16 mcr1;
- u16 pcr0;
- u16 rcerc;
- u16 rcerd;
- u16 xcerc;
- u16 xcerd;
- u16 rcere;
- u16 rcerf;
- u16 xcere;
- u16 xcerf;
- u16 rcerg;
- u16 rcerh;
- u16 xcerg;
- u16 xcerh;
- u16 xccr;
- u16 rccr;
-};
-
-struct omap_mcbsp_st_data;
-
-struct omap_mcbsp {
- struct device *dev;
- struct clk *fclk;
- spinlock_t lock;
- unsigned long phys_base;
- unsigned long phys_dma_base;
- void __iomem *io_base;
- u8 id;
- /*
- * Flags indicating is the bus already activated and configured by
- * another substream
- */
- int active;
- int configured;
- u8 free;
-
- int irq;
- int rx_irq;
- int tx_irq;
-
- /* Protect the field .free, while checking if the mcbsp is in use */
- struct omap_mcbsp_platform_data *pdata;
- struct omap_mcbsp_st_data *st_data;
- struct omap_mcbsp_reg_cfg cfg_regs;
- struct snd_dmaengine_dai_dma_data dma_data[2];
- unsigned int dma_req[2];
- int dma_op_mode;
- u16 max_tx_thres;
- u16 max_rx_thres;
- void *reg_cache;
- int reg_cache_size;
-
- unsigned int fmt;
- unsigned int in_freq;
- unsigned int latency[2];
- int clk_div;
- int wlen;
-
- struct pm_qos_request pm_qos_req;
-};
-
-static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
-{
- void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
-
- if (mcbsp->pdata->reg_size == 2) {
- ((u16 *)mcbsp->reg_cache)[reg] = (u16)val;
- writew_relaxed((u16)val, addr);
- } else {
- ((u32 *)mcbsp->reg_cache)[reg] = val;
- writel_relaxed(val, addr);
- }
-}
-
-static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg,
- bool from_cache)
-{
- void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
-
- if (mcbsp->pdata->reg_size == 2) {
- return !from_cache ? readw_relaxed(addr) :
- ((u16 *)mcbsp->reg_cache)[reg];
- } else {
- return !from_cache ? readl_relaxed(addr) :
- ((u32 *)mcbsp->reg_cache)[reg];
- }
-}
-
-#define MCBSP_READ(mcbsp, reg) \
- omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
-#define MCBSP_WRITE(mcbsp, reg, val) \
- omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
-#define MCBSP_READ_CACHE(mcbsp, reg) \
- omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
-
-
-/* Sidetone specific API */
-int omap_mcbsp_st_init(struct platform_device *pdev);
-void omap_mcbsp_st_cleanup(struct platform_device *pdev);
-
-int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp);
-int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp);
-
-#endif /* __OMAP_MCBSP_PRIV_H__ */
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0
-/*
- * McBSP Sidetone support
- *
- * Copyright (C) 2004 Nokia Corporation
- * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * Peter Ujfalusi <peter.ujfalusi@ti.com>
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-
-#include "omap-mcbsp.h"
-#include "omap-mcbsp-priv.h"
-
-/* OMAP3 sidetone control registers */
-#define OMAP_ST_REG_REV 0x00
-#define OMAP_ST_REG_SYSCONFIG 0x10
-#define OMAP_ST_REG_IRQSTATUS 0x18
-#define OMAP_ST_REG_IRQENABLE 0x1C
-#define OMAP_ST_REG_SGAINCR 0x24
-#define OMAP_ST_REG_SFIRCR 0x28
-#define OMAP_ST_REG_SSELCR 0x2C
-
-/********************** McBSP SSELCR bit definitions ***********************/
-#define SIDETONEEN BIT(10)
-
-/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/
-#define ST_AUTOIDLE BIT(0)
-
-/********************** McBSP Sidetone SGAINCR bit definitions *************/
-#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */
-#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */
-
-/********************** McBSP Sidetone SFIRCR bit definitions **************/
-#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */
-
-/********************** McBSP Sidetone SSELCR bit definitions **************/
-#define ST_SIDETONEEN BIT(0)
-#define ST_COEFFWREN BIT(1)
-#define ST_COEFFWRDONE BIT(2)
-
-struct omap_mcbsp_st_data {
- void __iomem *io_base_st;
- struct clk *mcbsp_iclk;
- bool running;
- bool enabled;
- s16 taps[128]; /* Sidetone filter coefficients */
- int nr_taps; /* Number of filter coefficients in use */
- s16 ch0gain;
- s16 ch1gain;
-};
-
-static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
-{
- writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
-}
-
-static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
-{
- return readl_relaxed(mcbsp->st_data->io_base_st + reg);
-}
-
-#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
-#define MCBSP_ST_WRITE(mcbsp, reg, val) \
- omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
-
-static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp)
-{
- unsigned int w;
-
- if (mcbsp->pdata->force_ick_on)
- mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true);
-
- /* Disable Sidetone clock auto-gating for normal operation */
- w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
- MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
-
- /* Enable McBSP Sidetone */
- w = MCBSP_READ(mcbsp, SSELCR);
- MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
-
- /* Enable Sidetone from Sidetone Core */
- w = MCBSP_ST_READ(mcbsp, SSELCR);
- MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
-}
-
-static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp)
-{
- unsigned int w;
-
- w = MCBSP_ST_READ(mcbsp, SSELCR);
- MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
-
- w = MCBSP_READ(mcbsp, SSELCR);
- MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
-
- /* Enable Sidetone clock auto-gating to reduce power consumption */
- w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
- MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
-
- if (mcbsp->pdata->force_ick_on)
- mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false);
-}
-
-static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
-{
- u16 val, i;
-
- val = MCBSP_ST_READ(mcbsp, SSELCR);
-
- if (val & ST_COEFFWREN)
- MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
-
- MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
-
- for (i = 0; i < 128; i++)
- MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
-
- i = 0;
-
- val = MCBSP_ST_READ(mcbsp, SSELCR);
- while (!(val & ST_COEFFWRDONE) && (++i < 1000))
- val = MCBSP_ST_READ(mcbsp, SSELCR);
-
- MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
-
- if (i == 1000)
- dev_err(mcbsp->dev, "McBSP FIR load error!\n");
-}
-
-static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp)
-{
- u16 w;
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- w = MCBSP_ST_READ(mcbsp, SSELCR);
-
- MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) |
- ST_CH1GAIN(st_data->ch1gain));
-}
-
-static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel,
- s16 chgain)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int ret = 0;
-
- if (!st_data)
- return -ENOENT;
-
- spin_lock_irq(&mcbsp->lock);
- if (channel == 0)
- st_data->ch0gain = chgain;
- else if (channel == 1)
- st_data->ch1gain = chgain;
- else
- ret = -EINVAL;
-
- if (st_data->enabled)
- omap_mcbsp_st_chgain(mcbsp);
- spin_unlock_irq(&mcbsp->lock);
-
- return ret;
-}
-
-static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel,
- s16 *chgain)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int ret = 0;
-
- if (!st_data)
- return -ENOENT;
-
- spin_lock_irq(&mcbsp->lock);
- if (channel == 0)
- *chgain = st_data->ch0gain;
- else if (channel == 1)
- *chgain = st_data->ch1gain;
- else
- ret = -EINVAL;
- spin_unlock_irq(&mcbsp->lock);
-
- return ret;
-}
-
-static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (!st_data)
- return -ENODEV;
-
- spin_lock_irq(&mcbsp->lock);
- st_data->enabled = 1;
- omap_mcbsp_st_start(mcbsp);
- spin_unlock_irq(&mcbsp->lock);
-
- return 0;
-}
-
-static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int ret = 0;
-
- if (!st_data)
- return -ENODEV;
-
- spin_lock_irq(&mcbsp->lock);
- omap_mcbsp_st_stop(mcbsp);
- st_data->enabled = 0;
- spin_unlock_irq(&mcbsp->lock);
-
- return ret;
-}
-
-static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (!st_data)
- return -ENODEV;
-
- return st_data->enabled;
-}
-
-static ssize_t st_taps_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- ssize_t status = 0;
- int i;
-
- spin_lock_irq(&mcbsp->lock);
- for (i = 0; i < st_data->nr_taps; i++)
- status += sprintf(&buf[status], (i ? ", %d" : "%d"),
- st_data->taps[i]);
- if (i)
- status += sprintf(&buf[status], "\n");
- spin_unlock_irq(&mcbsp->lock);
-
- return status;
-}
-
-static ssize_t st_taps_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
- int val, tmp, status, i = 0;
-
- spin_lock_irq(&mcbsp->lock);
- memset(st_data->taps, 0, sizeof(st_data->taps));
- st_data->nr_taps = 0;
-
- do {
- status = sscanf(buf, "%d%n", &val, &tmp);
- if (status < 0 || status == 0) {
- size = -EINVAL;
- goto out;
- }
- if (val < -32768 || val > 32767) {
- size = -EINVAL;
- goto out;
- }
- st_data->taps[i++] = val;
- buf += tmp;
- if (*buf != ',')
- break;
- buf++;
- } while (1);
-
- st_data->nr_taps = i;
-
-out:
- spin_unlock_irq(&mcbsp->lock);
-
- return size;
-}
-
-static DEVICE_ATTR_RW(st_taps);
-
-static const struct attribute *sidetone_attrs[] = {
- &dev_attr_st_taps.attr,
- NULL,
-};
-
-static const struct attribute_group sidetone_attr_group = {
- .attrs = (struct attribute **)sidetone_attrs,
-};
-
-int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (st_data->enabled && !st_data->running) {
- omap_mcbsp_st_fir_write(mcbsp, st_data->taps);
- omap_mcbsp_st_chgain(mcbsp);
-
- if (!mcbsp->free) {
- omap_mcbsp_st_on(mcbsp);
- st_data->running = 1;
- }
- }
-
- return 0;
-}
-
-int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp)
-{
- struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
-
- if (st_data->running) {
- if (!mcbsp->free) {
- omap_mcbsp_st_off(mcbsp);
- st_data->running = 0;
- }
- }
-
- return 0;
-}
-
-int omap_mcbsp_st_init(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
- struct omap_mcbsp_st_data *st_data;
- struct resource *res;
- int ret;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
- if (!res)
- return 0;
-
- st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
- if (!st_data)
- return -ENOMEM;
-
- st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick");
- if (IS_ERR(st_data->mcbsp_iclk)) {
- dev_warn(mcbsp->dev,
- "Failed to get ick, sidetone might be broken\n");
- st_data->mcbsp_iclk = NULL;
- }
-
- st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
- resource_size(res));
- if (!st_data->io_base_st)
- return -ENOMEM;
-
- ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
- if (ret)
- return ret;
-
- mcbsp->st_data = st_data;
-
- return 0;
-}
-
-void omap_mcbsp_st_cleanup(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
-
- if (mcbsp->st_data) {
- sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
- clk_put(mcbsp->st_data->mcbsp_iclk);
- }
-}
-
-static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int max = mc->max;
- int min = mc->min;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = min;
- uinfo->value.integer.max = max;
- return 0;
-}
-
-#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \
-static int \
-omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
- struct snd_ctl_elem_value *uc) \
-{ \
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
- struct soc_mixer_control *mc = \
- (struct soc_mixer_control *)kc->private_value; \
- int max = mc->max; \
- int min = mc->min; \
- int val = uc->value.integer.value[0]; \
- \
- if (val < min || val > max) \
- return -EINVAL; \
- \
- /* OMAP McBSP implementation uses index values 0..4 */ \
- return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \
-} \
- \
-static int \
-omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
- struct snd_ctl_elem_value *uc) \
-{ \
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
- s16 chgain; \
- \
- if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \
- return -EAGAIN; \
- \
- uc->value.integer.value[0] = chgain; \
- return 0; \
-}
-
-OMAP_MCBSP_ST_CHANNEL_VOLUME(0)
-OMAP_MCBSP_ST_CHANNEL_VOLUME(1)
-
-static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- u8 value = ucontrol->value.integer.value[0];
-
- if (value == omap_mcbsp_st_is_enabled(mcbsp))
- return 0;
-
- if (value)
- omap_mcbsp_st_enable(mcbsp);
- else
- omap_mcbsp_st_disable(mcbsp);
-
- return 1;
-}
-
-static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
-
- ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp);
- return 0;
-}
-
-#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
- xhandler_get, xhandler_put) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = omap_mcbsp_st_info_volsw, \
- .get = xhandler_get, .put = xhandler_put, \
- .private_value = (unsigned long)&(struct soc_mixer_control) \
- {.min = xmin, .max = xmax} }
-
-#define OMAP_MCBSP_ST_CONTROLS(port) \
-static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
-SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \
- omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \
-OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
- -32768, 32767, \
- omap_mcbsp_get_st_ch0_volume, \
- omap_mcbsp_set_st_ch0_volume), \
-OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
- -32768, 32767, \
- omap_mcbsp_get_st_ch1_volume, \
- omap_mcbsp_set_st_ch1_volume), \
-}
-
-OMAP_MCBSP_ST_CONTROLS(2);
-OMAP_MCBSP_ST_CONTROLS(3);
-
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
-{
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
-
- if (!mcbsp->st_data) {
- dev_warn(mcbsp->dev, "No sidetone data for port\n");
- return 0;
- }
-
- switch (port_id) {
- case 2: /* McBSP 2 */
- return snd_soc_add_dai_controls(cpu_dai,
- omap_mcbsp2_st_controls,
- ARRAY_SIZE(omap_mcbsp2_st_controls));
- case 3: /* McBSP 3 */
- return snd_soc_add_dai_controls(cpu_dai,
- omap_mcbsp3_st_controls,
- ARRAY_SIZE(omap_mcbsp3_st_controls));
- default:
- dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
- break;
- }
-
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
+++ /dev/null
-/*
- * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
- *
- * Copyright (C) 2008 Nokia Corporation
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * 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
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "omap-mcbsp-priv.h"
-#include "omap-mcbsp.h"
-#include "sdma-pcm.h"
-
-#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
-
-enum {
- OMAP_MCBSP_WORD_8 = 0,
- OMAP_MCBSP_WORD_12,
- OMAP_MCBSP_WORD_16,
- OMAP_MCBSP_WORD_20,
- OMAP_MCBSP_WORD_24,
- OMAP_MCBSP_WORD_32,
-};
-
-static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
-{
- dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
- dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2));
- dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1));
- dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2));
- dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1));
- dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2));
- dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1));
- dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2));
- dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1));
- dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2));
- dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1));
- dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2));
- dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1));
- dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0));
- dev_dbg(mcbsp->dev, "***********************\n");
-}
-
-static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
-{
- struct clk *fck_src;
- const char *src;
- int r;
-
- if (fck_src_id == MCBSP_CLKS_PAD_SRC)
- src = "pad_fck";
- else if (fck_src_id == MCBSP_CLKS_PRCM_SRC)
- src = "prcm_fck";
- else
- return -EINVAL;
-
- fck_src = clk_get(mcbsp->dev, src);
- if (IS_ERR(fck_src)) {
- dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
- return -EINVAL;
- }
-
- pm_runtime_put_sync(mcbsp->dev);
-
- r = clk_set_parent(mcbsp->fclk, fck_src);
- if (r) {
- dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n",
- src);
- clk_put(fck_src);
- return r;
- }
-
- pm_runtime_get_sync(mcbsp->dev);
-
- clk_put(fck_src);
-
- return 0;
-}
-
-static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data)
-{
- struct omap_mcbsp *mcbsp = data;
- u16 irqst;
-
- irqst = MCBSP_READ(mcbsp, IRQST);
- dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
-
- if (irqst & RSYNCERREN)
- dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
- if (irqst & RFSREN)
- dev_dbg(mcbsp->dev, "RX Frame Sync\n");
- if (irqst & REOFEN)
- dev_dbg(mcbsp->dev, "RX End Of Frame\n");
- if (irqst & RRDYEN)
- dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
- if (irqst & RUNDFLEN)
- dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
- if (irqst & ROVFLEN)
- dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
-
- if (irqst & XSYNCERREN)
- dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
- if (irqst & XFSXEN)
- dev_dbg(mcbsp->dev, "TX Frame Sync\n");
- if (irqst & XEOFEN)
- dev_dbg(mcbsp->dev, "TX End Of Frame\n");
- if (irqst & XRDYEN)
- dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
- if (irqst & XUNDFLEN)
- dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
- if (irqst & XOVFLEN)
- dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
- if (irqst & XEMPTYEOFEN)
- dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
-
- MCBSP_WRITE(mcbsp, IRQST, irqst);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data)
-{
- struct omap_mcbsp *mcbsp = data;
- u16 irqst_spcr2;
-
- irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2);
- dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
-
- if (irqst_spcr2 & XSYNC_ERR) {
- dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n",
- irqst_spcr2);
- /* Writing zero to XSYNC_ERR clears the IRQ */
- MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2));
- }
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data)
-{
- struct omap_mcbsp *mcbsp = data;
- u16 irqst_spcr1;
-
- irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1);
- dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
-
- if (irqst_spcr1 & RSYNC_ERR) {
- dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n",
- irqst_spcr1);
- /* Writing zero to RSYNC_ERR clears the IRQ */
- MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1));
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * omap_mcbsp_config simply write a config to the
- * appropriate McBSP.
- * You either call this function or set the McBSP registers
- * by yourself before calling omap_mcbsp_start().
- */
-static void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
- const struct omap_mcbsp_reg_cfg *config)
-{
- dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
- mcbsp->id, mcbsp->phys_base);
-
- /* We write the given config */
- MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
- MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
- MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
- MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
- MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
- MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
- MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
- MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
- MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
- MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
- MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
- if (mcbsp->pdata->has_ccr) {
- MCBSP_WRITE(mcbsp, XCCR, config->xccr);
- MCBSP_WRITE(mcbsp, RCCR, config->rccr);
- }
- /* Enable wakeup behavior */
- if (mcbsp->pdata->has_wakeup)
- MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
-
- /* Enable TX/RX sync error interrupts by default */
- if (mcbsp->irq)
- MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN |
- RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN);
-}
-
-/**
- * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register
- * @mcbsp: omap_mcbsp struct for the McBSP instance
- * @stream: Stream direction (playback/capture)
- *
- * Returns the address of mcbsp data transmit register or data receive register
- * to be used by DMA for transferring/receiving data
- */
-static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp,
- unsigned int stream)
-{
- int data_reg;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (mcbsp->pdata->reg_size == 2)
- data_reg = OMAP_MCBSP_REG_DXR1;
- else
- data_reg = OMAP_MCBSP_REG_DXR;
- } else {
- if (mcbsp->pdata->reg_size == 2)
- data_reg = OMAP_MCBSP_REG_DRR1;
- else
- data_reg = OMAP_MCBSP_REG_DRR;
- }
-
- return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step;
-}
-
-/*
- * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
- * The threshold parameter is 1 based, and it is converted (threshold - 1)
- * for the THRSH2 register.
- */
-static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
-{
- if (threshold && threshold <= mcbsp->max_tx_thres)
- MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
-}
-
-/*
- * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
- * The threshold parameter is 1 based, and it is converted (threshold - 1)
- * for the THRSH1 register.
- */
-static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
-{
- if (threshold && threshold <= mcbsp->max_rx_thres)
- MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
-}
-
-/*
- * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
- */
-static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp)
-{
- u16 buffstat;
-
- /* Returns the number of free locations in the buffer */
- buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
-
- /* Number of slots are different in McBSP ports */
- return mcbsp->pdata->buffer_size - buffstat;
-}
-
-/*
- * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
- * to reach the threshold value (when the DMA will be triggered to read it)
- */
-static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp)
-{
- u16 buffstat, threshold;
-
- /* Returns the number of used locations in the buffer */
- buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
- /* RX threshold */
- threshold = MCBSP_READ(mcbsp, THRSH1);
-
- /* Return the number of location till we reach the threshold limit */
- if (threshold <= buffstat)
- return 0;
- else
- return threshold - buffstat;
-}
-
-static int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
-{
- void *reg_cache;
- int err;
-
- reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL);
- if (!reg_cache)
- return -ENOMEM;
-
- spin_lock(&mcbsp->lock);
- if (!mcbsp->free) {
- dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id);
- err = -EBUSY;
- goto err_kfree;
- }
-
- mcbsp->free = false;
- mcbsp->reg_cache = reg_cache;
- spin_unlock(&mcbsp->lock);
-
- if(mcbsp->pdata->ops && mcbsp->pdata->ops->request)
- mcbsp->pdata->ops->request(mcbsp->id - 1);
-
- /*
- * Make sure that transmitter, receiver and sample-rate generator are
- * not running before activating IRQs.
- */
- MCBSP_WRITE(mcbsp, SPCR1, 0);
- MCBSP_WRITE(mcbsp, SPCR2, 0);
-
- if (mcbsp->irq) {
- err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
- "McBSP", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request IRQ\n");
- goto err_clk_disable;
- }
- } else {
- err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
- "McBSP TX", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
- goto err_clk_disable;
- }
-
- err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
- "McBSP RX", (void *)mcbsp);
- if (err != 0) {
- dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
- goto err_free_irq;
- }
- }
-
- return 0;
-err_free_irq:
- free_irq(mcbsp->tx_irq, (void *)mcbsp);
-err_clk_disable:
- if(mcbsp->pdata->ops && mcbsp->pdata->ops->free)
- mcbsp->pdata->ops->free(mcbsp->id - 1);
-
- /* Disable wakeup behavior */
- if (mcbsp->pdata->has_wakeup)
- MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
-
- spin_lock(&mcbsp->lock);
- mcbsp->free = true;
- mcbsp->reg_cache = NULL;
-err_kfree:
- spin_unlock(&mcbsp->lock);
- kfree(reg_cache);
-
- return err;
-}
-
-static void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
-{
- void *reg_cache;
-
- if(mcbsp->pdata->ops && mcbsp->pdata->ops->free)
- mcbsp->pdata->ops->free(mcbsp->id - 1);
-
- /* Disable wakeup behavior */
- if (mcbsp->pdata->has_wakeup)
- MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
-
- /* Disable interrupt requests */
- if (mcbsp->irq)
- MCBSP_WRITE(mcbsp, IRQEN, 0);
-
- if (mcbsp->irq) {
- free_irq(mcbsp->irq, (void *)mcbsp);
- } else {
- free_irq(mcbsp->rx_irq, (void *)mcbsp);
- free_irq(mcbsp->tx_irq, (void *)mcbsp);
- }
-
- reg_cache = mcbsp->reg_cache;
-
- /*
- * Select CLKS source from internal source unconditionally before
- * marking the McBSP port as free.
- * If the external clock source via MCBSP_CLKS pin has been selected the
- * system will refuse to enter idle if the CLKS pin source is not reset
- * back to internal source.
- */
- if (!mcbsp_omap1())
- omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC);
-
- spin_lock(&mcbsp->lock);
- if (mcbsp->free)
- dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
- else
- mcbsp->free = true;
- mcbsp->reg_cache = NULL;
- spin_unlock(&mcbsp->lock);
-
- kfree(reg_cache);
-}
-
-/*
- * Here we start the McBSP, by enabling transmitter, receiver or both.
- * If no transmitter or receiver is active prior calling, then sample-rate
- * generator and frame sync are started.
- */
-static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream)
-{
- int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
- int rx = !tx;
- int enable_srg = 0;
- u16 w;
-
- if (mcbsp->st_data)
- omap_mcbsp_st_start(mcbsp);
-
- /* Only enable SRG, if McBSP is master */
- w = MCBSP_READ_CACHE(mcbsp, PCR0);
- if (w & (FSXM | FSRM | CLKXM | CLKRM))
- enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
- MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
-
- if (enable_srg) {
- /* Start the sample generator */
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
- }
-
- /* Enable transmitter and receiver */
- tx &= 1;
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w | tx);
-
- rx &= 1;
- w = MCBSP_READ_CACHE(mcbsp, SPCR1);
- MCBSP_WRITE(mcbsp, SPCR1, w | rx);
-
- /*
- * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
- * REVISIT: 100us may give enough time for two CLKSRG, however
- * due to some unknown PM related, clock gating etc. reason it
- * is now at 500us.
- */
- udelay(500);
-
- if (enable_srg) {
- /* Start frame sync */
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
- }
-
- if (mcbsp->pdata->has_ccr) {
- /* Release the transmitter and receiver */
- w = MCBSP_READ_CACHE(mcbsp, XCCR);
- w &= ~(tx ? XDISABLE : 0);
- MCBSP_WRITE(mcbsp, XCCR, w);
- w = MCBSP_READ_CACHE(mcbsp, RCCR);
- w &= ~(rx ? RDISABLE : 0);
- MCBSP_WRITE(mcbsp, RCCR, w);
- }
-
- /* Dump McBSP Regs */
- omap_mcbsp_dump_reg(mcbsp);
-}
-
-static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream)
-{
- int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
- int rx = !tx;
- int idle;
- u16 w;
-
- /* Reset transmitter */
- tx &= 1;
- if (mcbsp->pdata->has_ccr) {
- w = MCBSP_READ_CACHE(mcbsp, XCCR);
- w |= (tx ? XDISABLE : 0);
- MCBSP_WRITE(mcbsp, XCCR, w);
- }
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
-
- /* Reset receiver */
- rx &= 1;
- if (mcbsp->pdata->has_ccr) {
- w = MCBSP_READ_CACHE(mcbsp, RCCR);
- w |= (rx ? RDISABLE : 0);
- MCBSP_WRITE(mcbsp, RCCR, w);
- }
- w = MCBSP_READ_CACHE(mcbsp, SPCR1);
- MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
-
- idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
- MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
-
- if (idle) {
- /* Reset the sample rate generator */
- w = MCBSP_READ_CACHE(mcbsp, SPCR2);
- MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
- }
-
- if (mcbsp->st_data)
- omap_mcbsp_st_stop(mcbsp);
-}
-
-#define max_thres(m) (mcbsp->pdata->buffer_size)
-#define valid_threshold(m, val) ((val) <= max_thres(m))
-#define THRESHOLD_PROP_BUILDER(prop) \
-static ssize_t prop##_show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
- \
- return sprintf(buf, "%u\n", mcbsp->prop); \
-} \
- \
-static ssize_t prop##_store(struct device *dev, \
- struct device_attribute *attr, \
- const char *buf, size_t size) \
-{ \
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
- unsigned long val; \
- int status; \
- \
- status = kstrtoul(buf, 0, &val); \
- if (status) \
- return status; \
- \
- if (!valid_threshold(mcbsp, val)) \
- return -EDOM; \
- \
- mcbsp->prop = val; \
- return size; \
-} \
- \
-static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store)
-
-THRESHOLD_PROP_BUILDER(max_tx_thres);
-THRESHOLD_PROP_BUILDER(max_rx_thres);
-
-static const char * const dma_op_modes[] = {
- "element", "threshold",
-};
-
-static ssize_t dma_op_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- int dma_op_mode, i = 0;
- ssize_t len = 0;
- const char * const *s;
-
- dma_op_mode = mcbsp->dma_op_mode;
-
- for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
- if (dma_op_mode == i)
- len += sprintf(buf + len, "[%s] ", *s);
- else
- len += sprintf(buf + len, "%s ", *s);
- }
- len += sprintf(buf + len, "\n");
-
- return len;
-}
-
-static ssize_t dma_op_mode_store(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t size)
-{
- struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
- int i;
-
- i = sysfs_match_string(dma_op_modes, buf);
- if (i < 0)
- return i;
-
- spin_lock_irq(&mcbsp->lock);
- if (!mcbsp->free) {
- size = -EBUSY;
- goto unlock;
- }
- mcbsp->dma_op_mode = i;
-
-unlock:
- spin_unlock_irq(&mcbsp->lock);
-
- return size;
-}
-
-static DEVICE_ATTR_RW(dma_op_mode);
-
-static const struct attribute *additional_attrs[] = {
- &dev_attr_max_tx_thres.attr,
- &dev_attr_max_rx_thres.attr,
- &dev_attr_dma_op_mode.attr,
- NULL,
-};
-
-static const struct attribute_group additional_attr_group = {
- .attrs = (struct attribute **)additional_attrs,
-};
-
-/*
- * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
- * 730 has only 2 McBSP, and both of them are MPU peripherals.
- */
-static int omap_mcbsp_init(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
- struct resource *res;
- int ret = 0;
-
- spin_lock_init(&mcbsp->lock);
- mcbsp->free = true;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- if (!res)
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(mcbsp->io_base))
- return PTR_ERR(mcbsp->io_base);
-
- mcbsp->phys_base = res->start;
- mcbsp->reg_cache_size = resource_size(res);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
- if (!res)
- mcbsp->phys_dma_base = mcbsp->phys_base;
- else
- mcbsp->phys_dma_base = res->start;
-
- /*
- * OMAP1, 2 uses two interrupt lines: TX, RX
- * OMAP2430, OMAP3 SoC have combined IRQ line as well.
- * OMAP4 and newer SoC only have the combined IRQ line.
- * Use the combined IRQ if available since it gives better debugging
- * possibilities.
- */
- mcbsp->irq = platform_get_irq_byname(pdev, "common");
- if (mcbsp->irq == -ENXIO) {
- mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
-
- if (mcbsp->tx_irq == -ENXIO) {
- mcbsp->irq = platform_get_irq(pdev, 0);
- mcbsp->tx_irq = 0;
- } else {
- mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
- mcbsp->irq = 0;
- }
- }
-
- if (!pdev->dev.of_node) {
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
- if (!res) {
- dev_err(&pdev->dev, "invalid tx DMA channel\n");
- return -ENODEV;
- }
- mcbsp->dma_req[0] = res->start;
- mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0];
-
- res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
- if (!res) {
- dev_err(&pdev->dev, "invalid rx DMA channel\n");
- return -ENODEV;
- }
- mcbsp->dma_req[1] = res->start;
- mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1];
- } else {
- mcbsp->dma_data[0].filter_data = "tx";
- mcbsp->dma_data[1].filter_data = "rx";
- }
-
- mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp,
- SNDRV_PCM_STREAM_PLAYBACK);
- mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp,
- SNDRV_PCM_STREAM_CAPTURE);
-
- mcbsp->fclk = clk_get(&pdev->dev, "fck");
- if (IS_ERR(mcbsp->fclk)) {
- ret = PTR_ERR(mcbsp->fclk);
- dev_err(mcbsp->dev, "unable to get fck: %d\n", ret);
- return ret;
- }
-
- mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
- if (mcbsp->pdata->buffer_size) {
- /*
- * Initially configure the maximum thresholds to a safe value.
- * The McBSP FIFO usage with these values should not go under
- * 16 locations.
- * If the whole FIFO without safety buffer is used, than there
- * is a possibility that the DMA will be not able to push the
- * new data on time, causing channel shifts in runtime.
- */
- mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
- mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
-
- ret = sysfs_create_group(&mcbsp->dev->kobj,
- &additional_attr_group);
- if (ret) {
- dev_err(mcbsp->dev,
- "Unable to create additional controls\n");
- goto err_thres;
- }
- }
-
- ret = omap_mcbsp_st_init(pdev);
- if (ret)
- goto err_st;
-
- return 0;
-
-err_st:
- if (mcbsp->pdata->buffer_size)
- sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
-err_thres:
- clk_put(mcbsp->fclk);
- return ret;
-}
-
-/*
- * Stream DMA parameters. DMA request line and port address are set runtime
- * since they are different between OMAP1 and later OMAPs
- */
-static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream,
- unsigned int packet_size)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- int words;
-
- /* No need to proceed further if McBSP does not have FIFO */
- if (mcbsp->pdata->buffer_size == 0)
- return;
-
- /*
- * Configure McBSP threshold based on either:
- * packet_size, when the sDMA is in packet mode, or based on the
- * period size in THRESHOLD mode, otherwise use McBSP threshold = 1
- * for mono streams.
- */
- if (packet_size)
- words = packet_size;
- else
- words = 1;
-
- /* Configure McBSP internal buffer usage */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- omap_mcbsp_set_tx_threshold(mcbsp, words);
- else
- omap_mcbsp_set_rx_threshold(mcbsp, words);
-}
-
-static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params,
- struct snd_pcm_hw_rule *rule)
-{
- struct snd_interval *buffer_size = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
- struct omap_mcbsp *mcbsp = rule->private;
- struct snd_interval frames;
- int size;
-
- snd_interval_any(&frames);
- size = mcbsp->pdata->buffer_size;
-
- frames.min = size / channels->min;
- frames.integer = 1;
- return snd_interval_refine(buffer_size, &frames);
-}
-
-static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- int err = 0;
-
- if (!cpu_dai->active)
- err = omap_mcbsp_request(mcbsp);
-
- /*
- * OMAP3 McBSP FIFO is word structured.
- * McBSP2 has 1024 + 256 = 1280 word long buffer,
- * McBSP1,3,4,5 has 128 word long buffer
- * This means that the size of the FIFO depends on the sample format.
- * For example on McBSP3:
- * 16bit samples: size is 128 * 2 = 256 bytes
- * 32bit samples: size is 128 * 4 = 512 bytes
- * It is simpler to place constraint for buffer and period based on
- * channels.
- * McBSP3 as example again (16 or 32 bit samples):
- * 1 channel (mono): size is 128 frames (128 words)
- * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
- * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
- */
- if (mcbsp->pdata->buffer_size) {
- /*
- * Rule for the buffer size. We should not allow
- * smaller buffer than the FIFO size to avoid underruns.
- * This applies only for the playback stream.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_pcm_hw_rule_add(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
- omap_mcbsp_hwrule_min_buffersize,
- mcbsp,
- SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-
- /* Make sure, that the period size is always even */
- snd_pcm_hw_constraint_step(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
- }
-
- return err;
-}
-
-static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
- int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-
- if (mcbsp->latency[stream2])
- pm_qos_update_request(&mcbsp->pm_qos_req,
- mcbsp->latency[stream2]);
- else if (mcbsp->latency[stream1])
- pm_qos_remove_request(&mcbsp->pm_qos_req);
-
- mcbsp->latency[stream1] = 0;
-
- if (!cpu_dai->active) {
- omap_mcbsp_free(mcbsp);
- mcbsp->configured = 0;
- }
-}
-
-static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req;
- int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
- int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
- int latency = mcbsp->latency[stream2];
-
- /* Prevent omap hardware from hitting off between FIFO fills */
- if (!latency || mcbsp->latency[stream1] < latency)
- latency = mcbsp->latency[stream1];
-
- if (pm_qos_request_active(pm_qos_req))
- pm_qos_update_request(pm_qos_req, latency);
- else if (latency)
- pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
-
- return 0;
-}
-
-static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- mcbsp->active++;
- omap_mcbsp_start(mcbsp, substream->stream);
- break;
-
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- omap_mcbsp_stop(mcbsp, substream->stream);
- mcbsp->active--;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static snd_pcm_sframes_t omap_mcbsp_dai_delay(
- struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- u16 fifo_use;
- snd_pcm_sframes_t delay;
-
- /* No need to proceed further if McBSP does not have FIFO */
- if (mcbsp->pdata->buffer_size == 0)
- return 0;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- fifo_use = omap_mcbsp_get_tx_delay(mcbsp);
- else
- fifo_use = omap_mcbsp_get_rx_delay(mcbsp);
-
- /*
- * Divide the used locations with the channel count to get the
- * FIFO usage in samples (don't care about partial samples in the
- * buffer).
- */
- delay = fifo_use / substream->runtime->channels;
-
- return delay;
-}
-
-static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *cpu_dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
- struct snd_dmaengine_dai_dma_data *dma_data;
- int wlen, channels, wpf;
- int pkt_size = 0;
- unsigned int format, div, framesize, master;
- unsigned int buffer_size = mcbsp->pdata->buffer_size;
-
- dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
- channels = params_channels(params);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- wlen = 16;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- wlen = 32;
- break;
- default:
- return -EINVAL;
- }
- if (buffer_size) {
- int latency;
-
- if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
- int period_words, max_thrsh;
- int divider = 0;
-
- period_words = params_period_bytes(params) / (wlen / 8);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- max_thrsh = mcbsp->max_tx_thres;
- else
- max_thrsh = mcbsp->max_rx_thres;
- /*
- * Use sDMA packet mode if McBSP is in threshold mode:
- * If period words less than the FIFO size the packet
- * size is set to the number of period words, otherwise
- * Look for the biggest threshold value which divides
- * the period size evenly.
- */
- divider = period_words / max_thrsh;
- if (period_words % max_thrsh)
- divider++;
- while (period_words % divider &&
- divider < period_words)
- divider++;
- if (divider == period_words)
- return -EINVAL;
-
- pkt_size = period_words / divider;
- } else if (channels > 1) {
- /* Use packet mode for non mono streams */
- pkt_size = channels;
- }
-
- latency = (buffer_size - pkt_size) / channels;
- latency = latency * USEC_PER_SEC /
- (params->rate_num / params->rate_den);
- mcbsp->latency[substream->stream] = latency;
-
- omap_mcbsp_set_threshold(substream, pkt_size);
- }
-
- dma_data->maxburst = pkt_size;
-
- if (mcbsp->configured) {
- /* McBSP already configured by another stream */
- return 0;
- }
-
- regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7));
- regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7));
- regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7));
- regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7));
- format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- wpf = channels;
- if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
- format == SND_SOC_DAIFMT_LEFT_J)) {
- /* Use dual-phase frames */
- regs->rcr2 |= RPHASE;
- regs->xcr2 |= XPHASE;
- /* Set 1 word per (McBSP) frame for phase1 and phase2 */
- wpf--;
- regs->rcr2 |= RFRLEN2(wpf - 1);
- regs->xcr2 |= XFRLEN2(wpf - 1);
- }
-
- regs->rcr1 |= RFRLEN1(wpf - 1);
- regs->xcr1 |= XFRLEN1(wpf - 1);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- /* Set word lengths */
- regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
- regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
- regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
- regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- /* Set word lengths */
- regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
- regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
- regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
- regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32);
- break;
- default:
- /* Unsupported PCM format */
- return -EINVAL;
- }
-
- /* In McBSP master modes, FRAME (i.e. sample rate) is generated
- * by _counting_ BCLKs. Calculate frame size in BCLKs */
- master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK;
- if (master == SND_SOC_DAIFMT_CBS_CFS) {
- div = mcbsp->clk_div ? mcbsp->clk_div : 1;
- framesize = (mcbsp->in_freq / div) / params_rate(params);
-
- if (framesize < wlen * channels) {
- printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
- "channels\n", __func__);
- return -EINVAL;
- }
- } else
- framesize = wlen * channels;
-
- /* Set FS period and length in terms of bit clock periods */
- regs->srgr2 &= ~FPER(0xfff);
- regs->srgr1 &= ~FWID(0xff);
- switch (format) {
- case SND_SOC_DAIFMT_I2S:
- case SND_SOC_DAIFMT_LEFT_J:
- regs->srgr2 |= FPER(framesize - 1);
- regs->srgr1 |= FWID((framesize >> 1) - 1);
- break;
- case SND_SOC_DAIFMT_DSP_A:
- case SND_SOC_DAIFMT_DSP_B:
- regs->srgr2 |= FPER(framesize - 1);
- regs->srgr1 |= FWID(0);
- break;
- }
-
- omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs);
- mcbsp->wlen = wlen;
- mcbsp->configured = 1;
-
- return 0;
-}
-
-/*
- * This must be called before _set_clkdiv and _set_sysclk since McBSP register
- * cache is initialized here
- */
-static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
- bool inv_fs = false;
-
- if (mcbsp->configured)
- return 0;
-
- mcbsp->fmt = fmt;
- memset(regs, 0, sizeof(*regs));
- /* Generic McBSP register settings */
- regs->spcr2 |= XINTM(3) | FREE;
- regs->spcr1 |= RINTM(3);
- /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */
- if (!mcbsp->pdata->has_ccr) {
- regs->rcr2 |= RFIG;
- regs->xcr2 |= XFIG;
- }
-
- /* Configure XCCR/RCCR only for revisions which have ccr registers */
- if (mcbsp->pdata->has_ccr) {
- regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
- regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- /* 1-bit data delay */
- regs->rcr2 |= RDATDLY(1);
- regs->xcr2 |= XDATDLY(1);
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- /* 0-bit data delay */
- regs->rcr2 |= RDATDLY(0);
- regs->xcr2 |= XDATDLY(0);
- regs->spcr1 |= RJUST(2);
- /* Invert FS polarity configuration */
- inv_fs = true;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- /* 1-bit data delay */
- regs->rcr2 |= RDATDLY(1);
- regs->xcr2 |= XDATDLY(1);
- /* Invert FS polarity configuration */
- inv_fs = true;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- /* 0-bit data delay */
- regs->rcr2 |= RDATDLY(0);
- regs->xcr2 |= XDATDLY(0);
- /* Invert FS polarity configuration */
- inv_fs = true;
- break;
- default:
- /* Unsupported data format */
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* McBSP master. Set FS and bit clocks as outputs */
- regs->pcr0 |= FSXM | FSRM |
- CLKXM | CLKRM;
- /* Sample rate generator drives the FS */
- regs->srgr2 |= FSGM;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- /* McBSP slave. FS clock as output */
- regs->srgr2 |= FSGM;
- regs->pcr0 |= FSXM | FSRM;
- break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* McBSP slave */
- break;
- default:
- /* Unsupported master/slave configuration */
- return -EINVAL;
- }
-
- /* Set bit clock (CLKX/CLKR) and FS polarities */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- /*
- * Normal BCLK + FS.
- * FS active low. TX data driven on falling edge of bit clock
- * and RX data sampled on rising edge of bit clock.
- */
- regs->pcr0 |= FSXP | FSRP |
- CLKXP | CLKRP;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- regs->pcr0 |= CLKXP | CLKRP;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- regs->pcr0 |= FSXP | FSRP;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- break;
- default:
- return -EINVAL;
- }
- if (inv_fs == true)
- regs->pcr0 ^= FSXP | FSRP;
-
- return 0;
-}
-
-static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
- int div_id, int div)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
-
- if (div_id != OMAP_MCBSP_CLKGDV)
- return -ENODEV;
-
- mcbsp->clk_div = div;
- regs->srgr1 &= ~CLKGDV(0xff);
- regs->srgr1 |= CLKGDV(div - 1);
-
- return 0;
-}
-
-static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
- int clk_id, unsigned int freq,
- int dir)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
- struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
- int err = 0;
-
- if (mcbsp->active) {
- if (freq == mcbsp->in_freq)
- return 0;
- else
- return -EBUSY;
- }
-
- mcbsp->in_freq = freq;
- regs->srgr2 &= ~CLKSM;
- regs->pcr0 &= ~SCLKME;
-
- switch (clk_id) {
- case OMAP_MCBSP_SYSCLK_CLK:
- regs->srgr2 |= CLKSM;
- break;
- case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
- if (mcbsp_omap1()) {
- err = -EINVAL;
- break;
- }
- err = omap2_mcbsp_set_clks_src(mcbsp,
- MCBSP_CLKS_PRCM_SRC);
- break;
- case OMAP_MCBSP_SYSCLK_CLKS_EXT:
- if (mcbsp_omap1()) {
- err = 0;
- break;
- }
- err = omap2_mcbsp_set_clks_src(mcbsp,
- MCBSP_CLKS_PAD_SRC);
- break;
-
- case OMAP_MCBSP_SYSCLK_CLKX_EXT:
- regs->srgr2 |= CLKSM;
- regs->pcr0 |= SCLKME;
- /*
- * If McBSP is master but yet the CLKX/CLKR pin drives the SRG,
- * disable output on those pins. This enables to inject the
- * reference clock through CLKX/CLKR. For this to work
- * set_dai_sysclk() _needs_ to be called after set_dai_fmt().
- */
- regs->pcr0 &= ~CLKXM;
- break;
- case OMAP_MCBSP_SYSCLK_CLKR_EXT:
- regs->pcr0 |= SCLKME;
- /* Disable ouput on CLKR pin in master mode */
- regs->pcr0 &= ~CLKRM;
- break;
- default:
- err = -ENODEV;
- }
-
- return err;
-}
-
-static const struct snd_soc_dai_ops mcbsp_dai_ops = {
- .startup = omap_mcbsp_dai_startup,
- .shutdown = omap_mcbsp_dai_shutdown,
- .prepare = omap_mcbsp_dai_prepare,
- .trigger = omap_mcbsp_dai_trigger,
- .delay = omap_mcbsp_dai_delay,
- .hw_params = omap_mcbsp_dai_hw_params,
- .set_fmt = omap_mcbsp_dai_set_dai_fmt,
- .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
- .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
-};
-
-static int omap_mcbsp_probe(struct snd_soc_dai *dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_enable(mcbsp->dev);
-
- snd_soc_dai_init_dma_data(dai,
- &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
- &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
-
- return 0;
-}
-
-static int omap_mcbsp_remove(struct snd_soc_dai *dai)
-{
- struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
-
- pm_runtime_disable(mcbsp->dev);
-
- return 0;
-}
-
-static struct snd_soc_dai_driver omap_mcbsp_dai = {
- .probe = omap_mcbsp_probe,
- .remove = omap_mcbsp_remove,
- .playback = {
- .channels_min = 1,
- .channels_max = 16,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 16,
- .rates = OMAP_MCBSP_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
- },
- .ops = &mcbsp_dai_ops,
-};
-
-static const struct snd_soc_component_driver omap_mcbsp_component = {
- .name = "omap-mcbsp",
-};
-
-static struct omap_mcbsp_platform_data omap2420_pdata = {
- .reg_step = 4,
- .reg_size = 2,
-};
-
-static struct omap_mcbsp_platform_data omap2430_pdata = {
- .reg_step = 4,
- .reg_size = 4,
- .has_ccr = true,
-};
-
-static struct omap_mcbsp_platform_data omap3_pdata = {
- .reg_step = 4,
- .reg_size = 4,
- .has_ccr = true,
- .has_wakeup = true,
-};
-
-static struct omap_mcbsp_platform_data omap4_pdata = {
- .reg_step = 4,
- .reg_size = 4,
- .has_ccr = true,
- .has_wakeup = true,
-};
-
-static const struct of_device_id omap_mcbsp_of_match[] = {
- {
- .compatible = "ti,omap2420-mcbsp",
- .data = &omap2420_pdata,
- },
- {
- .compatible = "ti,omap2430-mcbsp",
- .data = &omap2430_pdata,
- },
- {
- .compatible = "ti,omap3-mcbsp",
- .data = &omap3_pdata,
- },
- {
- .compatible = "ti,omap4-mcbsp",
- .data = &omap4_pdata,
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match);
-
-static int asoc_mcbsp_probe(struct platform_device *pdev)
-{
- struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev);
- struct omap_mcbsp *mcbsp;
- const struct of_device_id *match;
- int ret;
-
- match = of_match_device(omap_mcbsp_of_match, &pdev->dev);
- if (match) {
- struct device_node *node = pdev->dev.of_node;
- struct omap_mcbsp_platform_data *pdata_quirk = pdata;
- int buffer_size;
-
- pdata = devm_kzalloc(&pdev->dev,
- sizeof(struct omap_mcbsp_platform_data),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- memcpy(pdata, match->data, sizeof(*pdata));
- if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size))
- pdata->buffer_size = buffer_size;
- if (pdata_quirk)
- pdata->force_ick_on = pdata_quirk->force_ick_on;
- } else if (!pdata) {
- dev_err(&pdev->dev, "missing platform data.\n");
- return -EINVAL;
- }
- mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL);
- if (!mcbsp)
- return -ENOMEM;
-
- mcbsp->id = pdev->id;
- mcbsp->pdata = pdata;
- mcbsp->dev = &pdev->dev;
- platform_set_drvdata(pdev, mcbsp);
-
- ret = omap_mcbsp_init(pdev);
- if (ret)
- return ret;
-
- if (mcbsp->pdata->reg_size == 2) {
- omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
- omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
- }
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &omap_mcbsp_component,
- &omap_mcbsp_dai, 1);
- if (ret)
- return ret;
-
- return sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
-}
-
-static int asoc_mcbsp_remove(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
-
- if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
- mcbsp->pdata->ops->free(mcbsp->id);
-
- if (pm_qos_request_active(&mcbsp->pm_qos_req))
- pm_qos_remove_request(&mcbsp->pm_qos_req);
-
- if (mcbsp->pdata->buffer_size)
- sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
-
- omap_mcbsp_st_cleanup(pdev);
-
- clk_put(mcbsp->fclk);
-
- return 0;
-}
-
-static struct platform_driver asoc_mcbsp_driver = {
- .driver = {
- .name = "omap-mcbsp",
- .of_match_table = omap_mcbsp_of_match,
- },
-
- .probe = asoc_mcbsp_probe,
- .remove = asoc_mcbsp_remove,
-};
-
-module_platform_driver(asoc_mcbsp_driver);
-
-MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
-MODULE_DESCRIPTION("OMAP I2S SoC Interface");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:omap-mcbsp");
+++ /dev/null
-/*
- * omap-mcbsp.h
- *
- * Copyright (C) 2008 Nokia Corporation
- *
- * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
- * Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * 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
- *
- */
-
-#ifndef __OMAP_MCBSP_H__
-#define __OMAP_MCBSP_H__
-
-#include <sound/dmaengine_pcm.h>
-
-/* Source clocks for McBSP sample rate generator */
-enum omap_mcbsp_clksrg_clk {
- OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */
- OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */
- OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
- OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
- OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
-};
-
-/* McBSP dividers */
-enum omap_mcbsp_div {
- OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
-};
-
-int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id);
-
-#endif /* __OMAP_MCBSP_H__ */
+++ /dev/null
-/*
- * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port
- *
- * Copyright (C) 2009 - 2011 Texas Instruments
- *
- * Author: Misael Lopez Cruz <misael.lopez@ti.com>
- * Contact: Jorge Eduardo Candelaria <x0107209@ti.com>
- * Margarita Olaya <magi.olaya@ti.com>
- * Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * 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
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/slab.h>
-#include <linux/pm_runtime.h>
-#include <linux/of_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "omap-mcpdm.h"
-#include "sdma-pcm.h"
-
-struct mcpdm_link_config {
- u32 link_mask; /* channel mask for the direction */
- u32 threshold; /* FIFO threshold */
-};
-
-struct omap_mcpdm {
- struct device *dev;
- unsigned long phys_base;
- void __iomem *io_base;
- int irq;
- struct pm_qos_request pm_qos_req;
- int latency[2];
-
- struct mutex mutex;
-
- /* Playback/Capture configuration */
- struct mcpdm_link_config config[2];
-
- /* McPDM dn offsets for rx1, and 2 channels */
- u32 dn_rx_offset;
-
- /* McPDM needs to be restarted due to runtime reconfiguration */
- bool restart;
-
- /* pm state for suspend/resume handling */
- int pm_active_count;
-
- struct snd_dmaengine_dai_dma_data dma_data[2];
-};
-
-/*
- * Stream DMA parameters
- */
-
-static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val)
-{
- writel_relaxed(val, mcpdm->io_base + reg);
-}
-
-static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg)
-{
- return readl_relaxed(mcpdm->io_base + reg);
-}
-
-#ifdef DEBUG
-static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm)
-{
- dev_dbg(mcpdm->dev, "***********************\n");
- dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW));
- dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS));
- dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET));
- dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR));
- dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN));
- dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET));
- dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR));
- dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN));
- dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL));
- dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA));
- dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA));
- dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN));
- dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
- omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP));
- dev_dbg(mcpdm->dev, "***********************\n");
-}
-#else
-static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {}
-#endif
-
-/*
- * Enables the transfer through the PDM interface to/from the Phoenix
- * codec by enabling the corresponding UP or DN channels.
- */
-static void omap_mcpdm_start(struct omap_mcpdm *mcpdm)
-{
- u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
- u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask;
-
- ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
-
- ctrl |= link_mask;
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
-
- ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
-}
-
-/*
- * Disables the transfer through the PDM interface to/from the Phoenix
- * codec by disabling the corresponding UP or DN channels.
- */
-static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm)
-{
- u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
- u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK;
-
- ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
-
- ctrl &= ~(link_mask);
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
-
- ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
-
-}
-
-/*
- * Is the physical McPDM interface active.
- */
-static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
-{
- return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) &
- (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK);
-}
-
-/*
- * Configures McPDM uplink, and downlink for audio.
- * This function should be called before omap_mcpdm_start.
- */
-static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
-{
- u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
- MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
- MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
-
- /* Enable DN RX1/2 offset cancellation feature, if configured */
- if (mcpdm->dn_rx_offset) {
- u32 dn_offset = mcpdm->dn_rx_offset;
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
- dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN);
- omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
- }
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN,
- mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold);
- omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP,
- mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold);
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET,
- MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE);
-}
-
-/*
- * Cleans McPDM uplink, and downlink configuration.
- * This function should be called when the stream is closed.
- */
-static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm)
-{
- /* Disable irq request generation for downlink */
- omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
- MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL);
-
- /* Disable DMA request generation for downlink */
- omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE);
-
- /* Disable irq request generation for uplink */
- omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
- MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
-
- /* Disable DMA request generation for uplink */
- omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE);
-
- /* Disable RX1/2 offset cancellation */
- if (mcpdm->dn_rx_offset)
- omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0);
-}
-
-static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
-{
- struct omap_mcpdm *mcpdm = dev_id;
- int irq_status;
-
- irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS);
-
- /* Acknowledge irq event */
- omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status);
-
- if (irq_status & MCPDM_DN_IRQ_FULL)
- dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n");
-
- if (irq_status & MCPDM_DN_IRQ_EMPTY)
- dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n");
-
- if (irq_status & MCPDM_DN_IRQ)
- dev_dbg(mcpdm->dev, "DN (playback) write request\n");
-
- if (irq_status & MCPDM_UP_IRQ_FULL)
- dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n");
-
- if (irq_status & MCPDM_UP_IRQ_EMPTY)
- dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n");
-
- if (irq_status & MCPDM_UP_IRQ)
- dev_dbg(mcpdm->dev, "UP (capture) write request\n");
-
- return IRQ_HANDLED;
-}
-
-static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
-
- mutex_lock(&mcpdm->mutex);
-
- if (!dai->active)
- omap_mcpdm_open_streams(mcpdm);
-
- mutex_unlock(&mcpdm->mutex);
-
- return 0;
-}
-
-static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
- int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
- int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-
- mutex_lock(&mcpdm->mutex);
-
- if (!dai->active) {
- if (omap_mcpdm_active(mcpdm)) {
- omap_mcpdm_stop(mcpdm);
- omap_mcpdm_close_streams(mcpdm);
- mcpdm->config[0].link_mask = 0;
- mcpdm->config[1].link_mask = 0;
- }
- }
-
- if (mcpdm->latency[stream2])
- pm_qos_update_request(&mcpdm->pm_qos_req,
- mcpdm->latency[stream2]);
- else if (mcpdm->latency[stream1])
- pm_qos_remove_request(&mcpdm->pm_qos_req);
-
- mcpdm->latency[stream1] = 0;
-
- mutex_unlock(&mcpdm->mutex);
-}
-
-static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
- int stream = substream->stream;
- struct snd_dmaengine_dai_dma_data *dma_data;
- u32 threshold;
- int channels, latency;
- int link_mask = 0;
-
- channels = params_channels(params);
- switch (channels) {
- case 5:
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
- /* up to 3 channels for capture */
- return -EINVAL;
- link_mask |= 1 << 4;
- /* fall through */
- case 4:
- if (stream == SNDRV_PCM_STREAM_CAPTURE)
- /* up to 3 channels for capture */
- return -EINVAL;
- link_mask |= 1 << 3;
- /* fall through */
- case 3:
- link_mask |= 1 << 2;
- /* fall through */
- case 2:
- link_mask |= 1 << 1;
- /* fall through */
- case 1:
- link_mask |= 1 << 0;
- break;
- default:
- /* unsupported number of channels */
- return -EINVAL;
- }
-
- dma_data = snd_soc_dai_get_dma_data(dai, substream);
-
- threshold = mcpdm->config[stream].threshold;
- /* Configure McPDM channels, and DMA packet size */
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- link_mask <<= 3;
-
- /* If capture is not running assume a stereo stream to come */
- if (!mcpdm->config[!stream].link_mask)
- mcpdm->config[!stream].link_mask = 0x3;
-
- dma_data->maxburst =
- (MCPDM_DN_THRES_MAX - threshold) * channels;
- latency = threshold;
- } else {
- /* If playback is not running assume a stereo stream to come */
- if (!mcpdm->config[!stream].link_mask)
- mcpdm->config[!stream].link_mask = (0x3 << 3);
-
- dma_data->maxburst = threshold * channels;
- latency = (MCPDM_DN_THRES_MAX - threshold);
- }
-
- /*
- * The DMA must act to a DMA request within latency time (usec) to avoid
- * under/overflow
- */
- mcpdm->latency[stream] = latency * USEC_PER_SEC / params_rate(params);
-
- if (!mcpdm->latency[stream])
- mcpdm->latency[stream] = 10;
-
- /* Check if we need to restart McPDM with this stream */
- if (mcpdm->config[stream].link_mask &&
- mcpdm->config[stream].link_mask != link_mask)
- mcpdm->restart = true;
-
- mcpdm->config[stream].link_mask = link_mask;
-
- return 0;
-}
-
-static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
- struct pm_qos_request *pm_qos_req = &mcpdm->pm_qos_req;
- int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
- int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
- int latency = mcpdm->latency[stream2];
-
- /* Prevent omap hardware from hitting off between FIFO fills */
- if (!latency || mcpdm->latency[stream1] < latency)
- latency = mcpdm->latency[stream1];
-
- if (pm_qos_request_active(pm_qos_req))
- pm_qos_update_request(pm_qos_req, latency);
- else if (latency)
- pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
-
- if (!omap_mcpdm_active(mcpdm)) {
- omap_mcpdm_start(mcpdm);
- omap_mcpdm_reg_dump(mcpdm);
- } else if (mcpdm->restart) {
- omap_mcpdm_stop(mcpdm);
- omap_mcpdm_start(mcpdm);
- mcpdm->restart = false;
- omap_mcpdm_reg_dump(mcpdm);
- }
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
- .startup = omap_mcpdm_dai_startup,
- .shutdown = omap_mcpdm_dai_shutdown,
- .hw_params = omap_mcpdm_dai_hw_params,
- .prepare = omap_mcpdm_prepare,
-};
-
-static int omap_mcpdm_probe(struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
- int ret;
-
- pm_runtime_enable(mcpdm->dev);
-
- /* Disable lines while request is ongoing */
- pm_runtime_get_sync(mcpdm->dev);
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
-
- ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM",
- (void *)mcpdm);
-
- pm_runtime_put_sync(mcpdm->dev);
-
- if (ret) {
- dev_err(mcpdm->dev, "Request for IRQ failed\n");
- pm_runtime_disable(mcpdm->dev);
- }
-
- /* Configure McPDM threshold values */
- mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2;
- mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold =
- MCPDM_UP_THRES_MAX - 3;
-
- snd_soc_dai_init_dma_data(dai,
- &mcpdm->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
- &mcpdm->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
-
- return ret;
-}
-
-static int omap_mcpdm_remove(struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
-
- free_irq(mcpdm->irq, (void *)mcpdm);
- pm_runtime_disable(mcpdm->dev);
-
- if (pm_qos_request_active(&mcpdm->pm_qos_req))
- pm_qos_remove_request(&mcpdm->pm_qos_req);
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int omap_mcpdm_suspend(struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
-
- if (dai->active) {
- omap_mcpdm_stop(mcpdm);
- omap_mcpdm_close_streams(mcpdm);
- }
-
- mcpdm->pm_active_count = 0;
- while (pm_runtime_active(mcpdm->dev)) {
- pm_runtime_put_sync(mcpdm->dev);
- mcpdm->pm_active_count++;
- }
-
- return 0;
-}
-
-static int omap_mcpdm_resume(struct snd_soc_dai *dai)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
-
- if (mcpdm->pm_active_count) {
- while (mcpdm->pm_active_count--)
- pm_runtime_get_sync(mcpdm->dev);
-
- if (dai->active) {
- omap_mcpdm_open_streams(mcpdm);
- omap_mcpdm_start(mcpdm);
- }
- }
-
-
- return 0;
-}
-#else
-#define omap_mcpdm_suspend NULL
-#define omap_mcpdm_resume NULL
-#endif
-
-#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
-
-static struct snd_soc_dai_driver omap_mcpdm_dai = {
- .probe = omap_mcpdm_probe,
- .remove = omap_mcpdm_remove,
- .suspend = omap_mcpdm_suspend,
- .resume = omap_mcpdm_resume,
- .probe_order = SND_SOC_COMP_ORDER_LATE,
- .remove_order = SND_SOC_COMP_ORDER_EARLY,
- .playback = {
- .channels_min = 1,
- .channels_max = 5,
- .rates = OMAP_MCPDM_RATES,
- .formats = OMAP_MCPDM_FORMATS,
- .sig_bits = 24,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 3,
- .rates = OMAP_MCPDM_RATES,
- .formats = OMAP_MCPDM_FORMATS,
- .sig_bits = 24,
- },
- .ops = &omap_mcpdm_dai_ops,
-};
-
-static const struct snd_soc_component_driver omap_mcpdm_component = {
- .name = "omap-mcpdm",
-};
-
-void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
- u8 rx1, u8 rx2)
-{
- struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
-
- mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2);
-}
-EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets);
-
-static int asoc_mcpdm_probe(struct platform_device *pdev)
-{
- struct omap_mcpdm *mcpdm;
- struct resource *res;
- int ret;
-
- mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL);
- if (!mcpdm)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, mcpdm);
-
- mutex_init(&mcpdm->mutex);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
- if (res == NULL)
- return -ENOMEM;
-
- mcpdm->dma_data[0].addr = res->start + MCPDM_REG_DN_DATA;
- mcpdm->dma_data[1].addr = res->start + MCPDM_REG_UP_DATA;
-
- mcpdm->dma_data[0].filter_data = "dn_link";
- mcpdm->dma_data[1].filter_data = "up_link";
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
- mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(mcpdm->io_base))
- return PTR_ERR(mcpdm->io_base);
-
- mcpdm->irq = platform_get_irq(pdev, 0);
- if (mcpdm->irq < 0)
- return mcpdm->irq;
-
- mcpdm->dev = &pdev->dev;
-
- ret = devm_snd_soc_register_component(&pdev->dev,
- &omap_mcpdm_component,
- &omap_mcpdm_dai, 1);
- if (ret)
- return ret;
-
- return sdma_pcm_platform_register(&pdev->dev, "dn_link", "up_link");
-}
-
-static const struct of_device_id omap_mcpdm_of_match[] = {
- { .compatible = "ti,omap4-mcpdm", },
- { }
-};
-MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match);
-
-static struct platform_driver asoc_mcpdm_driver = {
- .driver = {
- .name = "omap-mcpdm",
- .of_match_table = omap_mcpdm_of_match,
- },
-
- .probe = asoc_mcpdm_probe,
-};
-
-module_platform_driver(asoc_mcpdm_driver);
-
-MODULE_ALIAS("platform:omap-mcpdm");
-MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
-MODULE_DESCRIPTION("OMAP PDM SoC Interface");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * omap-mcpdm.h
- *
- * Copyright (C) 2009 - 2011 Texas Instruments
- *
- * Contact: Misael Lopez Cruz <misael.lopez@ti.com>
- *
- * 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
- *
- */
-
-#ifndef __OMAP_MCPDM_H__
-#define __OMAP_MCPDM_H__
-
-#define MCPDM_REG_REVISION 0x00
-#define MCPDM_REG_SYSCONFIG 0x10
-#define MCPDM_REG_IRQSTATUS_RAW 0x24
-#define MCPDM_REG_IRQSTATUS 0x28
-#define MCPDM_REG_IRQENABLE_SET 0x2C
-#define MCPDM_REG_IRQENABLE_CLR 0x30
-#define MCPDM_REG_IRQWAKE_EN 0x34
-#define MCPDM_REG_DMAENABLE_SET 0x38
-#define MCPDM_REG_DMAENABLE_CLR 0x3C
-#define MCPDM_REG_DMAWAKEEN 0x40
-#define MCPDM_REG_CTRL 0x44
-#define MCPDM_REG_DN_DATA 0x48
-#define MCPDM_REG_UP_DATA 0x4C
-#define MCPDM_REG_FIFO_CTRL_DN 0x50
-#define MCPDM_REG_FIFO_CTRL_UP 0x54
-#define MCPDM_REG_DN_OFFSET 0x58
-
-/*
- * MCPDM_IRQ bit fields
- * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
- */
-
-#define MCPDM_DN_IRQ (1 << 0)
-#define MCPDM_DN_IRQ_EMPTY (1 << 1)
-#define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2)
-#define MCPDM_DN_IRQ_FULL (1 << 3)
-
-#define MCPDM_UP_IRQ (1 << 8)
-#define MCPDM_UP_IRQ_EMPTY (1 << 9)
-#define MCPDM_UP_IRQ_ALMST_FULL (1 << 10)
-#define MCPDM_UP_IRQ_FULL (1 << 11)
-
-#define MCPDM_DOWNLINK_IRQ_MASK 0x00F
-#define MCPDM_UPLINK_IRQ_MASK 0xF00
-
-/*
- * MCPDM_DMAENABLE bit fields
- */
-
-#define MCPDM_DMA_DN_ENABLE (1 << 0)
-#define MCPDM_DMA_UP_ENABLE (1 << 1)
-
-/*
- * MCPDM_CTRL bit fields
- */
-
-#define MCPDM_PDM_UPLINK_EN(x) (1 << (x - 1)) /* ch1 is at bit 0 */
-#define MCPDM_PDM_DOWNLINK_EN(x) (1 << (x + 2)) /* ch1 is at bit 3 */
-#define MCPDM_PDMOUTFORMAT (1 << 8)
-#define MCPDM_CMD_INT (1 << 9)
-#define MCPDM_STATUS_INT (1 << 10)
-#define MCPDM_SW_UP_RST (1 << 11)
-#define MCPDM_SW_DN_RST (1 << 12)
-#define MCPDM_WD_EN (1 << 14)
-#define MCPDM_PDM_UP_MASK 0x7
-#define MCPDM_PDM_DN_MASK (0x1f << 3)
-
-
-#define MCPDM_PDMOUTFORMAT_LJUST (0 << 8)
-#define MCPDM_PDMOUTFORMAT_RJUST (1 << 8)
-
-/*
- * MCPDM_FIFO_CTRL bit fields
- */
-
-#define MCPDM_UP_THRES_MAX 0xF
-#define MCPDM_DN_THRES_MAX 0xF
-
-/*
- * MCPDM_DN_OFFSET bit fields
- */
-
-#define MCPDM_DN_OFST_RX1_EN (1 << 0)
-#define MCPDM_DNOFST_RX1(x) ((x & 0x1f) << 1)
-#define MCPDM_DN_OFST_RX2_EN (1 << 8)
-#define MCPDM_DNOFST_RX2(x) ((x & 0x1f) << 9)
-
-void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
- u8 rx1, u8 rx2);
-
-#endif /* End of __OMAP_MCPDM_H__ */
+++ /dev/null
-/*
- * omap-twl4030.c -- SoC audio for TI SoC based boards with twl4030 codec
- *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
- * All rights reserved.
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- *
- * This driver replaces the following machine drivers:
- * omap3beagle (Author: Steve Sakoman <steve@sakoman.com>)
- * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
- * overo (Author: Steve Sakoman <steve@sakoman.com>)
- * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
- * zoom2 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
- * sdp3430 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
- *
- * 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
- *
- */
-
-#include <linux/platform_device.h>
-#include <linux/platform_data/omap-twl4030.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include "omap-mcbsp.h"
-
-struct omap_twl4030 {
- int jack_detect; /* board can detect jack events */
- struct snd_soc_jack hs_jack;
-};
-
-static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- unsigned int fmt;
-
- switch (params_channels(params)) {
- case 2: /* Stereo I2S mode */
- fmt = SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
- break;
- case 4: /* Four channel TDM mode */
- fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBM_CFM;
- break;
- default:
- return -EINVAL;
- }
-
- return snd_soc_runtime_set_dai_fmt(rtd, fmt);
-}
-
-static const struct snd_soc_ops omap_twl4030_ops = {
- .hw_params = omap_twl4030_hw_params,
-};
-
-static const struct snd_soc_dapm_widget dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
- SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
- SND_SOC_DAPM_HP("Headset Stereophone", NULL),
- SND_SOC_DAPM_SPK("Ext Spk", NULL),
- SND_SOC_DAPM_SPK("Carkit Spk", NULL),
-
- SND_SOC_DAPM_MIC("Main Mic", NULL),
- SND_SOC_DAPM_MIC("Sub Mic", NULL),
- SND_SOC_DAPM_MIC("Headset Mic", NULL),
- SND_SOC_DAPM_MIC("Carkit Mic", NULL),
- SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
- SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Headset Stereophone: HSOL, HSOR */
- {"Headset Stereophone", NULL, "HSOL"},
- {"Headset Stereophone", NULL, "HSOR"},
- /* External Speakers: HFL, HFR */
- {"Handsfree Spk", NULL, "HFL"},
- {"Handsfree Spk", NULL, "HFR"},
- /* External Speakers: PredrivL, PredrivR */
- {"Ext Spk", NULL, "PREDRIVEL"},
- {"Ext Spk", NULL, "PREDRIVER"},
- /* Carkit speakers: CARKITL, CARKITR */
- {"Carkit Spk", NULL, "CARKITL"},
- {"Carkit Spk", NULL, "CARKITR"},
- /* Earpiece */
- {"Earpiece Spk", NULL, "EARPIECE"},
-
- /* External Mics: MAINMIC, SUBMIC with bias */
- {"MAINMIC", NULL, "Main Mic"},
- {"Main Mic", NULL, "Mic Bias 1"},
- {"SUBMIC", NULL, "Sub Mic"},
- {"Sub Mic", NULL, "Mic Bias 2"},
- /* Headset Mic: HSMIC with bias */
- {"HSMIC", NULL, "Headset Mic"},
- {"Headset Mic", NULL, "Headset Mic Bias"},
- /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
- {"DIGIMIC0", NULL, "Digital0 Mic"},
- {"Digital0 Mic", NULL, "Mic Bias 1"},
- {"DIGIMIC1", NULL, "Digital1 Mic"},
- {"Digital1 Mic", NULL, "Mic Bias 2"},
- /* Carkit In: CARKITMIC */
- {"CARKITMIC", NULL, "Carkit Mic"},
- /* Aux In: AUXL, AUXR */
- {"AUXL", NULL, "Line In"},
- {"AUXR", NULL, "Line In"},
-};
-
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
- {
- .pin = "Headset Mic",
- .mask = SND_JACK_MICROPHONE,
- },
- {
- .pin = "Headset Stereophone",
- .mask = SND_JACK_HEADPHONE,
- },
-};
-
-/* Headset jack detection gpios */
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
- {
- .name = "hsdet-gpio",
- .report = SND_JACK_HEADSET,
- .debounce_time = 200,
- },
-};
-
-static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
- int connected, char *pin)
-{
- if (!connected)
- snd_soc_dapm_disable_pin(dapm, pin);
-}
-
-static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- struct snd_soc_dapm_context *dapm = &card->dapm;
- struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
- struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
- int ret = 0;
-
- /* Headset jack detection only if it is supported */
- if (priv->jack_detect > 0) {
- hs_jack_gpios[0].gpio = priv->jack_detect;
-
- ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
- SND_JACK_HEADSET, &priv->hs_jack,
- hs_jack_pins,
- ARRAY_SIZE(hs_jack_pins));
- if (ret)
- return ret;
-
- ret = snd_soc_jack_add_gpios(&priv->hs_jack,
- ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
- if (ret)
- return ret;
- }
-
- /*
- * NULL pdata means we booted with DT. In this case the routing is
- * provided and the card is fully routed, no need to mark pins.
- */
- if (!pdata || !pdata->custom_routing)
- return ret;
-
- /* Disable not connected paths if not used */
- twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
- twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
- twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
- twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
- twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
-
- twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
- twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
- twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
- twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
- twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
- twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
- twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
-
- return ret;
-}
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
- {
- .name = "TWL4030 HiFi",
- .stream_name = "TWL4030 HiFi",
- .cpu_dai_name = "omap-mcbsp.2",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-mcbsp.2",
- .codec_name = "twl4030-codec",
- .init = omap_twl4030_init,
- .ops = &omap_twl4030_ops,
- },
- {
- .name = "TWL4030 Voice",
- .stream_name = "TWL4030 Voice",
- .cpu_dai_name = "omap-mcbsp.3",
- .codec_dai_name = "twl4030-voice",
- .platform_name = "omap-mcbsp.3",
- .codec_name = "twl4030-codec",
- .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card omap_twl4030_card = {
- .owner = THIS_MODULE,
- .dai_link = omap_twl4030_dai_links,
- .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
-
- .dapm_widgets = dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static int omap_twl4030_probe(struct platform_device *pdev)
-{
- struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
- struct device_node *node = pdev->dev.of_node;
- struct snd_soc_card *card = &omap_twl4030_card;
- struct omap_twl4030 *priv;
- int ret = 0;
-
- card->dev = &pdev->dev;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
- if (priv == NULL)
- return -ENOMEM;
-
- if (node) {
- struct device_node *dai_node;
- struct property *prop;
-
- if (snd_soc_of_parse_card_name(card, "ti,model")) {
- dev_err(&pdev->dev, "Card name is not provided\n");
- return -ENODEV;
- }
-
- dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
- if (!dai_node) {
- dev_err(&pdev->dev, "McBSP node is not provided\n");
- return -EINVAL;
- }
- omap_twl4030_dai_links[0].cpu_dai_name = NULL;
- omap_twl4030_dai_links[0].cpu_of_node = dai_node;
-
- omap_twl4030_dai_links[0].platform_name = NULL;
- omap_twl4030_dai_links[0].platform_of_node = dai_node;
-
- dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
- if (!dai_node) {
- card->num_links = 1;
- } else {
- omap_twl4030_dai_links[1].cpu_dai_name = NULL;
- omap_twl4030_dai_links[1].cpu_of_node = dai_node;
-
- omap_twl4030_dai_links[1].platform_name = NULL;
- omap_twl4030_dai_links[1].platform_of_node = dai_node;
- }
-
- priv->jack_detect = of_get_named_gpio(node,
- "ti,jack-det-gpio", 0);
-
- /* Optional: audio routing can be provided */
- prop = of_find_property(node, "ti,audio-routing", NULL);
- if (prop) {
- ret = snd_soc_of_parse_audio_routing(card,
- "ti,audio-routing");
- if (ret)
- return ret;
-
- card->fully_routed = 1;
- }
- } else if (pdata) {
- if (pdata->card_name) {
- card->name = pdata->card_name;
- } else {
- dev_err(&pdev->dev, "Card name is not provided\n");
- return -ENODEV;
- }
-
- if (!pdata->voice_connected)
- card->num_links = 1;
-
- priv->jack_detect = pdata->jack_detect;
- } else {
- dev_err(&pdev->dev, "Missing pdata\n");
- return -ENODEV;
- }
-
- snd_soc_card_set_drvdata(card, priv);
- ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret) {
- dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
- ret);
- return ret;
- }
-
- return 0;
-}
-
-static const struct of_device_id omap_twl4030_of_match[] = {
- {.compatible = "ti,omap-twl4030", },
- { },
-};
-MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
-
-static struct platform_driver omap_twl4030_driver = {
- .driver = {
- .name = "omap-twl4030",
- .pm = &snd_soc_pm_ops,
- .of_match_table = omap_twl4030_of_match,
- },
- .probe = omap_twl4030_probe,
-};
-
-module_platform_driver(omap_twl4030_driver);
-
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:omap-twl4030");
+++ /dev/null
-/*
- * omap3pandora.c -- SoC audio for Pandora Handheld Console
- *
- * Author: Gražvydas Ignotas <notasas@gmail.com>
- *
- * 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
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/delay.h>
-#include <linux/regulator/consumer.h>
-#include <linux/module.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-
-#define OMAP3_PANDORA_DAC_POWER_GPIO 118
-#define OMAP3_PANDORA_AMP_POWER_GPIO 14
-
-#define PREFIX "ASoC omap3pandora: "
-
-static struct regulator *omap3pandora_dac_reg;
-
-static int omap3pandora_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
-
- /* Set the codec system clock for DAC and ADC */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- pr_err(PREFIX "can't set codec system clock\n");
- return ret;
- }
-
- /* Set McBSP clock to external */
- ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
- 256 * params_rate(params),
- SND_SOC_CLOCK_IN);
- if (ret < 0) {
- pr_err(PREFIX "can't set cpu system clock\n");
- return ret;
- }
-
- ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
- if (ret < 0) {
- pr_err(PREFIX "can't set SRG clock divider\n");
- return ret;
- }
-
- return 0;
-}
-
-static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- int ret;
-
- /*
- * The PCM1773 DAC datasheet requires 1ms delay between switching
- * VCC power on/off and /PD pin high/low
- */
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ret = regulator_enable(omap3pandora_dac_reg);
- if (ret) {
- dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret);
- return ret;
- }
- mdelay(1);
- gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
- } else {
- gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
- mdelay(1);
- regulator_disable(omap3pandora_dac_reg);
- }
-
- return 0;
-}
-
-static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- if (SND_SOC_DAPM_EVENT_ON(event))
- gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
- else
- gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
-
- return 0;
-}
-
-/*
- * Audio paths on Pandora board:
- *
- * |O| ---> PCM DAC +-> AMP -> Headphone Jack
- * |M| A +--------> Line Out
- * |A| <~~clk~~+
- * |P| <--- TWL4030 <--------- Line In and MICs
- */
-static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
- SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
- 0, 0, omap3pandora_dac_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
- 0, 0, NULL, 0, omap3pandora_hp_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line Out", NULL),
-
- SND_SOC_DAPM_MIC("Mic (internal)", NULL),
- SND_SOC_DAPM_MIC("Mic (external)", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
-};
-
-static const struct snd_soc_dapm_route omap3pandora_map[] = {
- {"PCM DAC", NULL, "APLL Enable"},
- {"Headphone Amplifier", NULL, "PCM DAC"},
- {"Line Out", NULL, "PCM DAC"},
- {"Headphone Jack", NULL, "Headphone Amplifier"},
-
- {"AUXL", NULL, "Line In"},
- {"AUXR", NULL, "Line In"},
-
- {"MAINMIC", NULL, "Mic (internal)"},
- {"Mic (internal)", NULL, "Mic Bias 1"},
-
- {"SUBMIC", NULL, "Mic (external)"},
- {"Mic (external)", NULL, "Mic Bias 2"},
-};
-
-static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
-
- /* All TWL4030 output pins are floating */
- snd_soc_dapm_nc_pin(dapm, "EARPIECE");
- snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
- snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
- snd_soc_dapm_nc_pin(dapm, "HSOL");
- snd_soc_dapm_nc_pin(dapm, "HSOR");
- snd_soc_dapm_nc_pin(dapm, "CARKITL");
- snd_soc_dapm_nc_pin(dapm, "CARKITR");
- snd_soc_dapm_nc_pin(dapm, "HFL");
- snd_soc_dapm_nc_pin(dapm, "HFR");
- snd_soc_dapm_nc_pin(dapm, "VIBRA");
-
- return 0;
-}
-
-static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
-
- /* Not comnnected */
- snd_soc_dapm_nc_pin(dapm, "HSMIC");
- snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
- snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
- snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
-
- return 0;
-}
-
-static const struct snd_soc_ops omap3pandora_ops = {
- .hw_params = omap3pandora_hw_params,
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link omap3pandora_dai[] = {
- {
- .name = "PCM1773",
- .stream_name = "HiFi Out",
- .cpu_dai_name = "omap-mcbsp.2",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-mcbsp.2",
- .codec_name = "twl4030-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &omap3pandora_ops,
- .init = omap3pandora_out_init,
- }, {
- .name = "TWL4030",
- .stream_name = "Line/Mic In",
- .cpu_dai_name = "omap-mcbsp.4",
- .codec_dai_name = "twl4030-hifi",
- .platform_name = "omap-mcbsp.4",
- .codec_name = "twl4030-codec",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS,
- .ops = &omap3pandora_ops,
- .init = omap3pandora_in_init,
- }
-};
-
-/* SoC card */
-static struct snd_soc_card snd_soc_card_omap3pandora = {
- .name = "omap3pandora",
- .owner = THIS_MODULE,
- .dai_link = omap3pandora_dai,
- .num_links = ARRAY_SIZE(omap3pandora_dai),
-
- .dapm_widgets = omap3pandora_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
- .dapm_routes = omap3pandora_map,
- .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
-};
-
-static struct platform_device *omap3pandora_snd_device;
-
-static int __init omap3pandora_soc_init(void)
-{
- int ret;
-
- if (!machine_is_omap3_pandora())
- return -ENODEV;
-
- pr_info("OMAP3 Pandora SoC init\n");
-
- ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
- if (ret) {
- pr_err(PREFIX "Failed to get DAC power GPIO\n");
- return ret;
- }
-
- ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
- if (ret) {
- pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
- goto fail0;
- }
-
- ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
- if (ret) {
- pr_err(PREFIX "Failed to get amp power GPIO\n");
- goto fail0;
- }
-
- ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
- if (ret) {
- pr_err(PREFIX "Failed to set amp power GPIO direction\n");
- goto fail1;
- }
-
- omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
- if (omap3pandora_snd_device == NULL) {
- pr_err(PREFIX "Platform device allocation failed\n");
- ret = -ENOMEM;
- goto fail1;
- }
-
- platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
-
- ret = platform_device_add(omap3pandora_snd_device);
- if (ret) {
- pr_err(PREFIX "Unable to add platform device\n");
- goto fail2;
- }
-
- omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
- if (IS_ERR(omap3pandora_dac_reg)) {
- pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
- dev_name(&omap3pandora_snd_device->dev),
- PTR_ERR(omap3pandora_dac_reg));
- ret = PTR_ERR(omap3pandora_dac_reg);
- goto fail3;
- }
-
- return 0;
-
-fail3:
- platform_device_del(omap3pandora_snd_device);
-fail2:
- platform_device_put(omap3pandora_snd_device);
-fail1:
- gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
-fail0:
- gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
- return ret;
-}
-module_init(omap3pandora_soc_init);
-
-static void __exit omap3pandora_soc_exit(void)
-{
- regulator_put(omap3pandora_dac_reg);
- platform_device_unregister(omap3pandora_snd_device);
- gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
- gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
-}
-module_exit(omap3pandora_soc_exit);
-
-MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
-MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * osk5912.c -- SoC audio for OSK 5912
- *
- * Copyright (C) 2008 Mistral Solutions
- *
- * Contact: Arun KS <arunks@mistralsolutions.com>
- *
- * 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
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <linux/gpio.h>
-#include <linux/module.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include "omap-mcbsp.h"
-#include "../codecs/tlv320aic23.h"
-
-#define CODEC_CLOCK 12000000
-
-static struct clk *tlv320aic23_mclk;
-
-static int osk_startup(struct snd_pcm_substream *substream)
-{
- return clk_enable(tlv320aic23_mclk);
-}
-
-static void osk_shutdown(struct snd_pcm_substream *substream)
-{
- clk_disable(tlv320aic23_mclk);
-}
-
-static int osk_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int err;
-
- /* Set the codec system clock for DAC and ADC */
- err =
- snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
-
- if (err < 0) {
- printk(KERN_ERR "can't set codec system clock\n");
- return err;
- }
-
- return err;
-}
-
-static const struct snd_soc_ops osk_ops = {
- .startup = osk_startup,
- .hw_params = osk_hw_params,
- .shutdown = osk_shutdown,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Headphone Jack", NULL, "LHPOUT"},
- {"Headphone Jack", NULL, "RHPOUT"},
-
- {"LLINEIN", NULL, "Line In"},
- {"RLINEIN", NULL, "Line In"},
-
- {"MICIN", NULL, "Mic Jack"},
-};
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link osk_dai = {
- .name = "TLV320AIC23",
- .stream_name = "AIC23",
- .cpu_dai_name = "omap-mcbsp.1",
- .codec_dai_name = "tlv320aic23-hifi",
- .platform_name = "omap-mcbsp.1",
- .codec_name = "tlv320aic23-codec",
- .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &osk_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_card_osk = {
- .name = "OSK5912",
- .owner = THIS_MODULE,
- .dai_link = &osk_dai,
- .num_links = 1,
-
- .dapm_widgets = tlv320aic23_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *osk_snd_device;
-
-static int __init osk_soc_init(void)
-{
- int err;
- u32 curRate;
- struct device *dev;
-
- if (!(machine_is_omap_osk()))
- return -ENODEV;
-
- osk_snd_device = platform_device_alloc("soc-audio", -1);
- if (!osk_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(osk_snd_device, &snd_soc_card_osk);
- err = platform_device_add(osk_snd_device);
- if (err)
- goto err1;
-
- dev = &osk_snd_device->dev;
-
- tlv320aic23_mclk = clk_get(dev, "mclk");
- if (IS_ERR(tlv320aic23_mclk)) {
- printk(KERN_ERR "Could not get mclk clock\n");
- err = PTR_ERR(tlv320aic23_mclk);
- goto err2;
- }
-
- /*
- * Configure 12 MHz output on MCLK.
- */
- curRate = (uint) clk_get_rate(tlv320aic23_mclk);
- if (curRate != CODEC_CLOCK) {
- if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) {
- printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n");
- err = -ECANCELED;
- goto err3;
- }
- }
-
- printk(KERN_INFO "MCLK = %d [%d]\n",
- (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
-
- return 0;
-
-err3:
- clk_put(tlv320aic23_mclk);
-err2:
- platform_device_del(osk_snd_device);
-err1:
- platform_device_put(osk_snd_device);
-
- return err;
-
-}
-
-static void __exit osk_soc_exit(void)
-{
- clk_put(tlv320aic23_mclk);
- platform_device_unregister(osk_snd_device);
-}
-
-module_init(osk_soc_init);
-module_exit(osk_soc_exit);
-
-MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
-MODULE_DESCRIPTION("ALSA SoC OSK 5912");
-MODULE_LICENSE("GPL");
+++ /dev/null
-/*
- * rx51.c -- SoC audio for Nokia RX-51
- *
- * Copyright (C) 2008 - 2009 Nokia Corporation
- *
- * Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
- * Eduardo Valentin <eduardo.valentin@nokia.com>
- * Jarkko Nikula <jarkko.nikula@bitmer.com>
- *
- * 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
- *
- */
-
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/platform_device.h>
-#include <linux/gpio/consumer.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/jack.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-#include <asm/mach-types.h>
-
-#include "omap-mcbsp.h"
-
-enum {
- RX51_JACK_DISABLED,
- RX51_JACK_TVOUT, /* tv-out with stereo output */
- RX51_JACK_HP, /* headphone: stereo output, no mic */
- RX51_JACK_HS, /* headset: stereo output with mic */
-};
-
-struct rx51_audio_pdata {
- struct gpio_desc *tvout_selection_gpio;
- struct gpio_desc *jack_detection_gpio;
- struct gpio_desc *eci_sw_gpio;
- struct gpio_desc *speaker_amp_gpio;
-};
-
-static int rx51_spk_func;
-static int rx51_dmic_func;
-static int rx51_jack_func;
-
-static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
-{
- struct snd_soc_card *card = dapm->card;
- struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
- int hp = 0, hs = 0, tvout = 0;
-
- switch (rx51_jack_func) {
- case RX51_JACK_TVOUT:
- tvout = 1;
- hp = 1;
- break;
- case RX51_JACK_HS:
- hs = 1;
- case RX51_JACK_HP:
- hp = 1;
- break;
- }
-
- snd_soc_dapm_mutex_lock(dapm);
-
- if (rx51_spk_func)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
- if (rx51_dmic_func)
- snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
- if (hp)
- snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
- if (hs)
- snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
- else
- snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
-
- gpiod_set_value(pdata->tvout_selection_gpio, tvout);
-
- snd_soc_dapm_sync_unlocked(dapm);
-
- snd_soc_dapm_mutex_unlock(dapm);
-}
-
-static int rx51_startup(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_card *card = rtd->card;
-
- snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
- rx51_ext_control(&card->dapm);
-
- return 0;
-}
-
-static int rx51_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
-
- /* Set the codec system clock for DAC and ADC */
- return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000,
- SND_SOC_CLOCK_IN);
-}
-
-static const struct snd_soc_ops rx51_ops = {
- .startup = rx51_startup,
- .hw_params = rx51_hw_params,
-};
-
-static int rx51_get_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = rx51_spk_func;
-
- return 0;
-}
-
-static int rx51_set_spk(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (rx51_spk_func == ucontrol->value.enumerated.item[0])
- return 0;
-
- rx51_spk_func = ucontrol->value.enumerated.item[0];
- rx51_ext_control(&card->dapm);
-
- return 1;
-}
-
-static int rx51_spk_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct snd_soc_card *card = dapm->card;
- struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
-
- gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio,
- !!SND_SOC_DAPM_EVENT_ON(event));
-
- return 0;
-}
-
-static int rx51_get_input(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = rx51_dmic_func;
-
- return 0;
-}
-
-static int rx51_set_input(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (rx51_dmic_func == ucontrol->value.enumerated.item[0])
- return 0;
-
- rx51_dmic_func = ucontrol->value.enumerated.item[0];
- rx51_ext_control(&card->dapm);
-
- return 1;
-}
-
-static int rx51_get_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = rx51_jack_func;
-
- return 0;
-}
-
-static int rx51_set_jack(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
-
- if (rx51_jack_func == ucontrol->value.enumerated.item[0])
- return 0;
-
- rx51_jack_func = ucontrol->value.enumerated.item[0];
- rx51_ext_control(&card->dapm);
-
- return 1;
-}
-
-static struct snd_soc_jack rx51_av_jack;
-
-static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
- {
- .name = "avdet-gpio",
- .report = SND_JACK_HEADSET,
- .invert = 1,
- .debounce_time = 200,
- },
-};
-
-static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
- SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
- SND_SOC_DAPM_MIC("DMic", NULL),
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_MIC("HS Mic", NULL),
- SND_SOC_DAPM_LINE("FM Transmitter", NULL),
- SND_SOC_DAPM_SPK("Earphone", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
- {"Ext Spk", NULL, "HPLOUT"},
- {"Ext Spk", NULL, "HPROUT"},
- {"Ext Spk", NULL, "HPLCOM"},
- {"Ext Spk", NULL, "HPRCOM"},
- {"FM Transmitter", NULL, "LLOUT"},
- {"FM Transmitter", NULL, "RLOUT"},
-
- {"Headphone Jack", NULL, "TPA6130A2 HPLEFT"},
- {"Headphone Jack", NULL, "TPA6130A2 HPRIGHT"},
- {"TPA6130A2 LEFTIN", NULL, "LLOUT"},
- {"TPA6130A2 RIGHTIN", NULL, "RLOUT"},
-
- {"DMic Rate 64", NULL, "DMic"},
- {"DMic", NULL, "Mic Bias"},
-
- {"b LINE2R", NULL, "MONO_LOUT"},
- {"Earphone", NULL, "b HPLOUT"},
-
- {"LINE1L", NULL, "HS Mic"},
- {"HS Mic", NULL, "b Mic Bias"},
-};
-
-static const char * const spk_function[] = {"Off", "On"};
-static const char * const input_function[] = {"ADC", "Digital Mic"};
-static const char * const jack_function[] = {
- "Off", "TV-OUT", "Headphone", "Headset"
-};
-
-static const struct soc_enum rx51_enum[] = {
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
- SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
-};
-
-static const struct snd_kcontrol_new aic34_rx51_controls[] = {
- SOC_ENUM_EXT("Speaker Function", rx51_enum[0],
- rx51_get_spk, rx51_set_spk),
- SOC_ENUM_EXT("Input Select", rx51_enum[1],
- rx51_get_input, rx51_set_input),
- SOC_ENUM_EXT("Jack Function", rx51_enum[2],
- rx51_get_jack, rx51_set_jack),
- SOC_DAPM_PIN_SWITCH("FM Transmitter"),
- SOC_DAPM_PIN_SWITCH("Earphone"),
-};
-
-static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
- struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
- int err;
-
- snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
-
- err = omap_mcbsp_st_add_controls(rtd, 2);
- if (err < 0) {
- dev_err(card->dev, "Failed to add MCBSP controls\n");
- return err;
- }
-
- /* AV jack detection */
- err = snd_soc_card_jack_new(rtd->card, "AV Jack",
- SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
- &rx51_av_jack, NULL, 0);
- if (err) {
- dev_err(card->dev, "Failed to add AV Jack\n");
- return err;
- }
-
- /* prepare gpio for snd_soc_jack_add_gpios */
- rx51_av_jack_gpios[0].gpio = desc_to_gpio(pdata->jack_detection_gpio);
- devm_gpiod_put(card->dev, pdata->jack_detection_gpio);
-
- err = snd_soc_jack_add_gpios(&rx51_av_jack,
- ARRAY_SIZE(rx51_av_jack_gpios),
- rx51_av_jack_gpios);
- if (err) {
- dev_err(card->dev, "Failed to add GPIOs\n");
- return err;
- }
-
- return err;
-}
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link rx51_dai[] = {
- {
- .name = "TLV320AIC34",
- .stream_name = "AIC34",
- .cpu_dai_name = "omap-mcbsp.2",
- .codec_dai_name = "tlv320aic3x-hifi",
- .platform_name = "omap-mcbsp.2",
- .codec_name = "tlv320aic3x-codec.2-0018",
- .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
- SND_SOC_DAIFMT_CBM_CFM,
- .init = rx51_aic34_init,
- .ops = &rx51_ops,
- },
-};
-
-static struct snd_soc_aux_dev rx51_aux_dev[] = {
- {
- .name = "TLV320AIC34b",
- .codec_name = "tlv320aic3x-codec.2-0019",
- },
- {
- .name = "TPA61320A2",
- .codec_name = "tpa6130a2.2-0060",
- },
-};
-
-static struct snd_soc_codec_conf rx51_codec_conf[] = {
- {
- .dev_name = "tlv320aic3x-codec.2-0019",
- .name_prefix = "b",
- },
- {
- .dev_name = "tpa6130a2.2-0060",
- .name_prefix = "TPA6130A2",
- },
-};
-
-/* Audio card */
-static struct snd_soc_card rx51_sound_card = {
- .name = "RX-51",
- .owner = THIS_MODULE,
- .dai_link = rx51_dai,
- .num_links = ARRAY_SIZE(rx51_dai),
- .aux_dev = rx51_aux_dev,
- .num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
- .codec_conf = rx51_codec_conf,
- .num_configs = ARRAY_SIZE(rx51_codec_conf),
- .fully_routed = true,
-
- .controls = aic34_rx51_controls,
- .num_controls = ARRAY_SIZE(aic34_rx51_controls),
- .dapm_widgets = aic34_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(aic34_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static int rx51_soc_probe(struct platform_device *pdev)
-{
- struct rx51_audio_pdata *pdata;
- struct device_node *np = pdev->dev.of_node;
- struct snd_soc_card *card = &rx51_sound_card;
- int err;
-
- if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900"))
- return -ENODEV;
-
- card->dev = &pdev->dev;
-
- if (np) {
- struct device_node *dai_node;
-
- dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0);
- if (!dai_node) {
- dev_err(&pdev->dev, "McBSP node is not provided\n");
- return -EINVAL;
- }
- rx51_dai[0].cpu_dai_name = NULL;
- rx51_dai[0].platform_name = NULL;
- rx51_dai[0].cpu_of_node = dai_node;
- rx51_dai[0].platform_of_node = dai_node;
-
- dai_node = of_parse_phandle(np, "nokia,audio-codec", 0);
- if (!dai_node) {
- dev_err(&pdev->dev, "Codec node is not provided\n");
- return -EINVAL;
- }
- rx51_dai[0].codec_name = NULL;
- rx51_dai[0].codec_of_node = dai_node;
-
- dai_node = of_parse_phandle(np, "nokia,audio-codec", 1);
- if (!dai_node) {
- dev_err(&pdev->dev, "Auxiliary Codec node is not provided\n");
- return -EINVAL;
- }
- rx51_aux_dev[0].codec_name = NULL;
- rx51_aux_dev[0].codec_of_node = dai_node;
- rx51_codec_conf[0].dev_name = NULL;
- rx51_codec_conf[0].of_node = dai_node;
-
- dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0);
- if (!dai_node) {
- dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
- return -EINVAL;
- }
- rx51_aux_dev[1].codec_name = NULL;
- rx51_aux_dev[1].codec_of_node = dai_node;
- rx51_codec_conf[1].dev_name = NULL;
- rx51_codec_conf[1].of_node = dai_node;
- }
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (pdata == NULL)
- return -ENOMEM;
-
- snd_soc_card_set_drvdata(card, pdata);
-
- pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
- "tvout-selection",
- GPIOD_OUT_LOW);
- if (IS_ERR(pdata->tvout_selection_gpio)) {
- dev_err(card->dev, "could not get tvout selection gpio\n");
- return PTR_ERR(pdata->tvout_selection_gpio);
- }
-
- pdata->jack_detection_gpio = devm_gpiod_get(card->dev,
- "jack-detection",
- GPIOD_ASIS);
- if (IS_ERR(pdata->jack_detection_gpio)) {
- dev_err(card->dev, "could not get jack detection gpio\n");
- return PTR_ERR(pdata->jack_detection_gpio);
- }
-
- pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch",
- GPIOD_OUT_HIGH);
- if (IS_ERR(pdata->eci_sw_gpio)) {
- dev_err(card->dev, "could not get eci switch gpio\n");
- return PTR_ERR(pdata->eci_sw_gpio);
- }
-
- pdata->speaker_amp_gpio = devm_gpiod_get(card->dev,
- "speaker-amplifier",
- GPIOD_OUT_LOW);
- if (IS_ERR(pdata->speaker_amp_gpio)) {
- dev_err(card->dev, "could not get speaker enable gpio\n");
- return PTR_ERR(pdata->speaker_amp_gpio);
- }
-
- err = devm_snd_soc_register_card(card->dev, card);
- if (err) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err);
- return err;
- }
-
- return 0;
-}
-
-#if defined(CONFIG_OF)
-static const struct of_device_id rx51_audio_of_match[] = {
- { .compatible = "nokia,n900-audio", },
- {},
-};
-MODULE_DEVICE_TABLE(of, rx51_audio_of_match);
-#endif
-
-static struct platform_driver rx51_soc_driver = {
- .driver = {
- .name = "rx51-audio",
- .of_match_table = of_match_ptr(rx51_audio_of_match),
- },
- .probe = rx51_soc_probe,
-};
-
-module_platform_driver(rx51_soc_driver);
-
-MODULE_AUTHOR("Nokia Corporation");
-MODULE_DESCRIPTION("ALSA SoC Nokia RX-51");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:rx51-audio");
+++ /dev/null
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-#include <linux/omap-dmaengine.h>
-
-#include "sdma-pcm.h"
-
-static const struct snd_pcm_hardware sdma_pcm_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
- SNDRV_PCM_INFO_INTERLEAVED,
- .period_bytes_min = 32,
- .period_bytes_max = 64 * 1024,
- .buffer_bytes_max = 128 * 1024,
- .periods_min = 2,
- .periods_max = 255,
-};
-
-static const struct snd_dmaengine_pcm_config sdma_dmaengine_pcm_config = {
- .pcm_hardware = &sdma_pcm_hardware,
- .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
- .compat_filter_fn = omap_dma_filter_fn,
- .prealloc_buffer_size = 128 * 1024,
-};
-
-int sdma_pcm_platform_register(struct device *dev,
- char *txdmachan, char *rxdmachan)
-{
- struct snd_dmaengine_pcm_config *config;
- unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT;
-
- /* Standard names for the directions: 'tx' and 'rx' */
- if (!txdmachan && !rxdmachan)
- return devm_snd_dmaengine_pcm_register(dev,
- &sdma_dmaengine_pcm_config,
- flags);
-
- config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL);
- if (!config)
- return -ENOMEM;
-
- *config = sdma_dmaengine_pcm_config;
-
- if (!txdmachan || !rxdmachan) {
- /* One direction only PCM */
- flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
- if (!txdmachan) {
- txdmachan = rxdmachan;
- rxdmachan = NULL;
- }
- }
-
- config->chan_names[0] = txdmachan;
- config->chan_names[1] = rxdmachan;
-
- return devm_snd_dmaengine_pcm_register(dev, config, flags);
-}
-EXPORT_SYMBOL_GPL(sdma_pcm_platform_register);
-
-MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
-MODULE_DESCRIPTION("sDMA PCM ASoC platform driver");
-MODULE_LICENSE("GPL v2");
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
- * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
- */
-
-#ifndef __SDMA_PCM_H__
-#define __SDMA_PCM_H__
-
-#if IS_ENABLED(CONFIG_SND_SDMA_SOC)
-int sdma_pcm_platform_register(struct device *dev,
- char *txdmachan, char *rxdmachan);
-#else
-static inline int sdma_pcm_platform_register(struct device *dev,
- char *txdmachan, char *rxdmachan)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_SND_SDMA_SOC */
-
-#endif /* __SDMA_PCM_H__ */
--- /dev/null
+menu "Audio support for Texas Instruments SoCs"
+depends on DMA_OMAP || TI_EDMA || COMPILE_TEST
+
+config SND_SOC_TI_EDMA_PCM
+ tristate
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
+config SND_SOC_TI_SDMA_PCM
+ tristate
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
+comment "Texas Instruments DAI support for:"
+config SND_SOC_DAVINCI_ASP
+ tristate "daVinci Audio Serial Port (ASP) or McBSP suport"
+ depends on ARCH_DAVINCI || COMPILE_TEST
+ select SND_SOC_TI_EDMA_PCM
+ help
+ Say Y or M here if you want audio support via daVinci ASP or McBSP.
+ The driver only implements the ASP support which is a subset of
+ daVinci McBSP (w/o the multichannel support).
+
+config SND_SOC_DAVINCI_MCASP
+ tristate "Multichannel Audio Serial Port (McASP) support"
+ select SND_SOC_TI_EDMA_PCM if TI_EDMA
+ select SND_SOC_TI_SDMA_PCM if DMA_OMAP
+ help
+ Say Y or M here if you want to have support for McASP IP found in
+ various Texas Instruments SoCs like:
+ - daVinci devices
+ - Sitara line of SoCs (AM335x, AM438x, etc)
+ - DRA7x devices
+ - Keystone devices
+
+config SND_SOC_DAVINCI_VCIF
+ tristate "daVinci Voice Interface (VCIF) suport"
+ depends on ARCH_DAVINCI || COMPILE_TEST
+ select SND_SOC_TI_EDMA_PCM
+ help
+ Say Y or M here if you want audio support via daVinci VCIF.
+
+config SND_SOC_OMAP_DMIC
+ tristate "Digital Microphone Module (DMIC) support"
+ depends on ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST
+ select SND_SOC_TI_SDMA_PCM
+ help
+ Say Y or M here if you want to have support for DMIC IP found in
+ OMAP4 and OMAP5.
+
+config SND_OMAP_SOC_MCBSP
+ tristate
+
+config SND_SOC_OMAP_MCBSP
+ tristate "Multichannel Buffered Serial Port (McBSP) support"
+ depends on ARCH_OMAP || ARCH_OMAP1 || COMPILE_TEST
+ select SND_SOC_TI_SDMA_PCM
+ select SND_OMAP_SOC_MCBSP
+ help
+ Say Y or M here if you want to have support for McBSP IP found in
+ Texas Instruments OMAP1/2/3/4/5 SoCs.
+
+config SND_SOC_OMAP_MCPDM
+ tristate "Multichannel PDM Controller (McPDM) support"
+ depends on ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST
+ select SND_SOC_TI_SDMA_PCM
+ help
+ Say Y or M here if you want to have support for McPDM IP found in
+ OMAP4 and OMAP5.
+
+comment "Audio support for boards with Texas Instruments SoCs"
+config SND_SOC_NOKIA_N810
+ tristate "SoC Audio support for Nokia N810"
+ depends on MACH_NOKIA_N810 && I2C
+ select SND_SOC_OMAP_MCBSP
+ select SND_SOC_TLV320AIC3X
+ help
+ Say Y or M if you want to add support for SoC audio on Nokia N810.
+
+config SND_SOC_NOKIA_RX51
+ tristate "SoC Audio support for Nokia RX-51"
+ depends on ARCH_OMAP3 && I2C && GPIOLIB
+ select SND_SOC_OMAP_MCBSP
+ select SND_SOC_TLV320AIC3X
+ select SND_SOC_TPA6130A2
+ help
+ Say Y or M if you want to add support for SoC audio on Nokia RX-51
+ hardware. This is also known as Nokia N900 product.
+
+config SND_SOC_OMAP3_PANDORA
+ tristate "SoC Audio support for OMAP3 Pandora"
+ depends on ARCH_OMAP3
+ depends on TWL4030_CORE
+ select SND_SOC_OMAP_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y or M if you want to add support for SoC audio on the OMAP3 Pandora.
+
+config SND_SOC_OMAP3_TWL4030
+ tristate "SoC Audio support for OMAP3 based boards with twl4030 codec"
+ depends on ARCH_OMAP3 || COMPILE_TEST
+ depends on TWL4030_CORE
+ select SND_SOC_OMAP_MCBSP
+ select SND_SOC_TWL4030
+ help
+ Say Y or M if you want to add support for SoC audio on OMAP3 based
+ boards using twl4030 as codec. This driver currently supports:
+ - Beagleboard or Devkit8000
+ - Gumstix Overo or CompuLab CM-T35/CM-T3730
+ - IGEP v2
+ - OMAP3EVM
+ - SDP3430
+ - Zoom2
+
+config SND_SOC_OMAP_ABE_TWL6040
+ tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
+ depends on TWL6040_CORE && COMMON_CLK
+ depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
+ select SND_SOC_OMAP_DMIC
+ select SND_SOC_OMAP_MCPDM
+ select SND_SOC_TWL6040
+ help
+ Say Y or M if you want to add support for SoC audio on OMAP boards
+ using ABE and twl6040 codec. This driver currently supports:
+ - SDP4430/Blaze boards
+ - PandaBoard (4430)
+ - PandaBoardES (4460)
+ - OMAP5 uEVM
+
+config SND_SOC_OMAP_AMS_DELTA
+ tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
+ depends on MACH_AMS_DELTA && TTY
+ select SND_SOC_OMAP_MCBSP
+ select SND_SOC_CX20442
+ help
+ Say Y or M if you want to add support for SoC audio device
+ connected to a handset and a speakerphone found on Amstrad E3 (Delta)
+ videophone.
+
+ Note that in order to get those devices fully supported, you have to
+ build the kernel with standard serial port driver included and
+ configured for at least 4 ports. Then, from userspace, you must load
+ a line discipline #19 on the modem (ttyS3) serial line. The simplest
+ way to achieve this is to install util-linux-ng and use the included
+ ldattach utility. This can be started automatically from udev,
+ a simple rule like this one should do the trick (it does for me):
+ ACTION=="add", KERNEL=="controlC0", \
+ RUN+="/usr/sbin/ldattach 19 /dev/ttyS3"
+
+config SND_SOC_OMAP_HDMI
+ tristate "OMAP4/5 HDMI audio support"
+ depends on OMAP4_DSS_HDMI || OMAP5_DSS_HDMI || COMPILE_TEST
+ select SND_SOC_TI_SDMA_PCM
+ help
+ For HDMI audio to work OMAPDSS HDMI support should be
+ enabled.
+ The hdmi audio driver implements cpu-dai component using the
+ callbacks provided by OMAPDSS and registers the component
+ under DSS HDMI device. Omap-pcm is registered for platform
+ component also under DSS HDMI device. Dummy codec is used as
+ as codec component. The hdmi audio driver implements also
+ the card and registers it under its own platform device.
+ The device for the driver is registered by OMAPDSS hdmi
+ driver.
+
+config SND_SOC_OMAP_OSK5912
+ tristate "SoC Audio support for omap osk5912"
+ depends on MACH_OMAP_OSK && I2C
+ select SND_SOC_OMAP_MCBSP
+ select SND_SOC_TLV320AIC23_I2C
+ help
+ Say Y or M if you want to add support for SoC audio on osk5912.
+
+config SND_SOC_DAVINCI_EVM
+ tristate "SoC Audio support for DaVinci EVMs"
+ depends on ARCH_DAVINCI && I2C
+ select SND_SOC_DAVINCI_ASP if MACH_DAVINCI_DM355_EVM
+ select SND_SOC_DAVINCI_ASP if SND_SOC_DM365_AIC3X_CODEC
+ select SND_SOC_DAVINCI_VCIF if SND_SOC_DM365_VOICE_CODEC
+ select SND_SOC_DAVINCI_ASP if MACH_DAVINCI_EVM # DM6446
+ select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DM6467_EVM
+ select SND_SOC_SPDIF if MACH_DAVINCI_DM6467_EVM
+ select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DA830_EVM
+ select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DA850_EVM
+ select SND_SOC_TLV320AIC3X
+ help
+ Say Y if you want to add support for SoC audio on the following TI
+ DaVinci EVM platforms:
+ - DM355
+ - DM365
+ - DM6446
+ - DM6447
+ - DM830
+ - DM850
+
+choice
+ prompt "DM365 codec select"
+ depends on SND_SOC_DAVINCI_EVM
+ depends on MACH_DAVINCI_DM365_EVM
+
+config SND_SOC_DM365_AIC3X_CODEC
+ bool "Audio Codec - AIC3101"
+ help
+ Say Y if you want to add support for AIC3101 audio codec
+
+config SND_SOC_DM365_VOICE_CODEC
+ bool "Voice Codec - CQ93VC"
+ select MFD_DAVINCI_VOICECODEC
+ select SND_SOC_CQ0093VC
+ help
+ Say Y if you want to add support for SoC On-chip voice codec
+endchoice
+
+menu "Deprecated kconfig options:"
+
+config SND_AM33XX_SOC_EVM
+ tristate "SoC Audio for the AM33XX chip based boards"
+ depends on SOC_AM33XX && I2C
+ select SND_SOC_TLV320AIC3X
+ select SND_SOC_DAVINCI_MCASP
+ help
+ Say Y or M if you want to add support for SoC audio on AM33XX
+ boards using McASP and TLV320AIC3X codec. For example AM335X-EVM,
+ AM335X-EVMSK, and BeagelBone with AudioCape boards have this
+ setup.
+
+config SND_DAVINCI_SOC_EVM
+ tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
+ depends on ARCH_DAVINCI && I2C
+ depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM
+ select SND_SOC_DAVINCI_EVM
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DaVinci DM6446, DM355 or DM365 EVM platforms.
+
+choice
+ prompt "DM365 codec select"
+ depends on SND_DAVINCI_SOC_EVM
+ depends on MACH_DAVINCI_DM365_EVM
+
+config SND_DM365_AIC3X_CODEC
+ tristate "Audio Codec - AIC3101"
+ select SND_SOC_DM365_AIC3X_CODEC
+ help
+ Say Y if you want to add support for AIC3101 audio codec
+
+config SND_DM365_VOICE_CODEC
+ tristate "Voice Codec - CQ93VC"
+ select SND_SOC_DM365_VOICE_CODEC
+ help
+ Say Y if you want to add support for SoC On-chip voice codec
+endchoice
+
+config SND_DM6467_SOC_EVM
+ tristate "SoC Audio support for DaVinci DM6467 EVM"
+ depends on MACH_DAVINCI_DM6467_EVM && I2C
+ select SND_SOC_DAVINCI_EVM
+
+ help
+ Say Y if you want to add support for SoC audio on TI
+
+config SND_DA830_SOC_EVM
+ tristate "SoC Audio support for DA830/OMAP-L137 EVM"
+ depends on MACH_DAVINCI_DA830_EVM && I2C
+ select SND_SOC_DAVINCI_EVM
+
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DA830/OMAP-L137 EVM
+
+config SND_DA850_SOC_EVM
+ tristate "SoC Audio support for DA850/OMAP-L138 EVM"
+ depends on MACH_DAVINCI_DA850_EVM && I2C
+ select SND_SOC_DAVINCI_EVM
+ help
+ Say Y if you want to add support for SoC audio on TI
+ DA850/OMAP-L138 EVM
+
+config SND_OMAP_SOC_HDMI_AUDIO
+ tristate "HDMI audio support for OMAP4+ based SoCs"
+ select SND_SOC_OMAP_HDMI
+ help
+ For HDMI audio to work OMAPDSS HDMI support should be
+ enabled.
+ The hdmi audio driver implements cpu-dai component using the
+ callbacks provided by OMAPDSS and registers the component
+ under DSS HDMI device. Omap-pcm is registered for platform
+ component also under DSS HDMI device. Dummy codec is used as
+ as codec component. The hdmi audio driver implements also
+ the card and registers it under its own platform device.
+ The device for the driver is registered by OMAPDSS hdmi
+ driver.
+
+config SND_OMAP_SOC_N810
+ tristate "SoC Audio support for Nokia N810"
+ depends on MACH_NOKIA_N810 && I2C
+ select SND_SOC_NOKIA_N810
+ help
+ Say Y if you want to add support for SoC audio on Nokia N810.
+
+config SND_OMAP_SOC_RX51
+ tristate "SoC Audio support for Nokia N900 (RX-51)"
+ depends on ARM && I2C && GPIOLIB && ARCH_OMAP3
+ select SND_SOC_NOKIA_RX51
+ help
+ Say Y if you want to add support for SoC audio on Nokia N900
+ cellphone.
+
+config SND_OMAP_SOC_AMS_DELTA
+ tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
+ depends on MACH_AMS_DELTA && TTY
+ select SND_SOC_OMAP_AMS_DELTA
+ help
+ Say Y if you want to add support for SoC audio device connected to
+ a handset and a speakerphone found on Amstrad E3 (Delta) videophone.
+
+ Note that in order to get those devices fully supported, you have to
+ build the kernel with standard serial port driver included and
+ configured for at least 4 ports. Then, from userspace, you must load
+ a line discipline #19 on the modem (ttyS3) serial line. The simplest
+ way to achieve this is to install util-linux-ng and use the included
+ ldattach utility. This can be started automatically from udev,
+ a simple rule like this one should do the trick (it does for me):
+ ACTION=="add", KERNEL=="controlC0", \
+ RUN+="/usr/sbin/ldattach 19 /dev/ttyS3"
+
+config SND_OMAP_SOC_OSK5912
+ tristate "SoC Audio support for omap osk5912"
+ depends on MACH_OMAP_OSK && I2C
+ select SND_SOC_OMAP_OSK5912
+ help
+ Say Y if you want to add support for SoC audio on osk5912.
+
+config SND_OMAP_SOC_OMAP_TWL4030
+ tristate "SoC Audio support for TI SoC based boards with twl4030 codec"
+ depends on TWL4030_CORE
+ select SND_SOC_OMAP3_TWL4030
+ help
+ Say Y if you want to add support for SoC audio on TI SoC based boards
+ using twl4030 as c codec. This driver currently supports:
+ - Beagleboard or Devkit8000
+ - Gumstix Overo or CompuLab CM-T35/CM-T3730
+ - IGEP v2
+ - OMAP3EVM
+ - SDP3430
+ - Zoom2
+
+config SND_OMAP_SOC_OMAP_ABE_TWL6040
+ tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
+ depends on TWL6040_CORE && COMMON_CLK
+ depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST
+ select SND_SOC_OMAP_ABE_TWL6040
+ help
+ Say Y if you want to add support for SoC audio on OMAP boards using
+ ABE and twl6040 codec. This driver currently supports:
+ - SDP4430/Blaze boards
+ - PandaBoard (4430)
+ - PandaBoardES (4460)
+ - omap5-uevm (5432)
+
+config SND_OMAP_SOC_OMAP3_PANDORA
+ tristate "SoC Audio support for OMAP3 Pandora"
+ depends on TWL4030_CORE
+ select SND_SOC_OMAP3_PANDORA
+ help
+ Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
+endmenu
+
+endmenu
+
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+
+# Platform drivers
+snd-soc-ti-edma-objs := edma-pcm.o
+snd-soc-ti-sdma-objs := sdma-pcm.o
+
+obj-$(CONFIG_SND_SOC_TI_EDMA_PCM) += snd-soc-ti-edma.o
+obj-$(CONFIG_SND_SOC_TI_SDMA_PCM) += snd-soc-ti-sdma.o
+
+# CPU DAI drivers
+snd-soc-davinci-asp-objs := davinci-i2s.o
+snd-soc-davinci-mcasp-objs := davinci-mcasp.o
+snd-soc-davinci-vcif-objs := davinci-vcif.o
+snd-soc-omap-dmic-objs := omap-dmic.o
+snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o
+snd-soc-omap-mcpdm-objs := omap-mcpdm.o
+
+obj-$(CONFIG_SND_SOC_DAVINCI_ASP) += snd-soc-davinci-asp.o
+obj-$(CONFIG_SND_SOC_DAVINCI_MCASP) += snd-soc-davinci-mcasp.o
+obj-$(CONFIG_SND_SOC_DAVINCI_VCIF) += snd-soc-davinci-vcif.o
+obj-$(CONFIG_SND_SOC_OMAP_DMIC) += snd-soc-omap-dmic.o
+obj-$(CONFIG_SND_SOC_OMAP_MCBSP) += snd-soc-omap-mcbsp.o
+obj-$(CONFIG_SND_SOC_OMAP_MCPDM) += snd-soc-omap-mcpdm.o
+
+# Machine drivers
+snd-soc-davinci-evm-objs := davinci-evm.o
+snd-soc-n810-objs := n810.o
+snd-soc-rx51-objs := rx51.o
+snd-soc-omap3pandora-objs := omap3pandora.o
+snd-soc-omap-twl4030-objs := omap-twl4030.o
+snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
+snd-soc-ams-delta-objs := ams-delta.o
+snd-soc-omap-hdmi-objs := omap-hdmi.o
+snd-soc-osk5912-objs := osk5912.o
+
+obj-$(CONFIG_SND_SOC_DAVINCI_EVM) += snd-soc-davinci-evm.o
+obj-$(CONFIG_SND_SOC_NOKIA_N810) += snd-soc-n810.o
+obj-$(CONFIG_SND_SOC_NOKIA_RX51) += snd-soc-rx51.o
+obj-$(CONFIG_SND_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
+obj-$(CONFIG_SND_SOC_OMAP3_TWL4030) += snd-soc-omap-twl4030.o
+obj-$(CONFIG_SND_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
+obj-$(CONFIG_SND_SOC_OMAP_AMS_DELTA) += snd-soc-ams-delta.o
+obj-$(CONFIG_SND_SOC_OMAP_HDMI) += snd-soc-omap-hdmi.o
+obj-$(CONFIG_SND_SOC_OMAP_OSK5912) += snd-soc-osk5912.o
--- /dev/null
+/*
+ * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone
+ *
+ * Copyright (C) 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Initially based on sound/soc/omap/osk5912.x
+ * Copyright (C) 2008 Mistral Solutions
+ *
+ * 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
+ *
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/tty.h>
+#include <linux/module.h>
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "../codecs/cx20442.h"
+
+/* Board specific DAPM widgets */
+static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+ /* Handset */
+ SND_SOC_DAPM_MIC("Mouthpiece", NULL),
+ SND_SOC_DAPM_HP("Earpiece", NULL),
+ /* Handsfree/Speakerphone */
+ SND_SOC_DAPM_MIC("Microphone", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* How they are connected to codec pins */
+static const struct snd_soc_dapm_route ams_delta_audio_map[] = {
+ {"TELIN", NULL, "Mouthpiece"},
+ {"Earpiece", NULL, "TELOUT"},
+
+ {"MIC", NULL, "Microphone"},
+ {"Speaker", NULL, "SPKOUT"},
+};
+
+/*
+ * Controls, functional after the modem line discipline is activated.
+ */
+
+/* Virtual switch: audio input/output constellations */
+static const char *ams_delta_audio_mode[] =
+ {"Mixed", "Handset", "Handsfree", "Speakerphone"};
+
+/* Selection <-> pin translation */
+#define AMS_DELTA_MOUTHPIECE 0
+#define AMS_DELTA_EARPIECE 1
+#define AMS_DELTA_MICROPHONE 2
+#define AMS_DELTA_SPEAKER 3
+#define AMS_DELTA_AGC 4
+
+#define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \
+ (1 << AMS_DELTA_MICROPHONE))
+#define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \
+ (1 << AMS_DELTA_EARPIECE))
+#define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \
+ (1 << AMS_DELTA_SPEAKER))
+#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
+
+static const unsigned short ams_delta_audio_mode_pins[] = {
+ AMS_DELTA_MIXED,
+ AMS_DELTA_HANDSET,
+ AMS_DELTA_HANDSFREE,
+ AMS_DELTA_SPEAKERPHONE,
+};
+
+static unsigned short ams_delta_audio_agc;
+
+/*
+ * Used for passing a codec structure pointer
+ * from the board initialization code to the tty line discipline.
+ */
+static struct snd_soc_component *cx20442_codec;
+
+static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+ struct soc_enum *control = (struct soc_enum *)kcontrol->private_value;
+ unsigned short pins;
+ int pin, changed = 0;
+
+ /* Refuse any mode changes if we are not able to control the codec. */
+ if (!cx20442_codec->card->pop_time)
+ return -EUNATCH;
+
+ if (ucontrol->value.enumerated.item[0] >= control->items)
+ return -EINVAL;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Translate selection to bitmap */
+ pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]];
+
+ /* Setup pins after corresponding bits if changed */
+ pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE));
+
+ if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_EARPIECE));
+ if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_MICROPHONE));
+ if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_SPEAKER));
+ if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) {
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
+ }
+ pin = !!(pins & (1 << AMS_DELTA_AGC));
+ if (pin != ams_delta_audio_agc) {
+ ams_delta_audio_agc = pin;
+ changed = 1;
+ if (pin)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
+ }
+
+ if (changed)
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+ unsigned short pins, mode;
+
+ pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") <<
+ AMS_DELTA_MOUTHPIECE) |
+ (snd_soc_dapm_get_pin_status(dapm, "Earpiece") <<
+ AMS_DELTA_EARPIECE));
+ if (pins)
+ pins |= (snd_soc_dapm_get_pin_status(dapm, "Microphone") <<
+ AMS_DELTA_MICROPHONE);
+ else
+ pins = ((snd_soc_dapm_get_pin_status(dapm, "Microphone") <<
+ AMS_DELTA_MICROPHONE) |
+ (snd_soc_dapm_get_pin_status(dapm, "Speaker") <<
+ AMS_DELTA_SPEAKER) |
+ (ams_delta_audio_agc << AMS_DELTA_AGC));
+
+ for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++)
+ if (pins == ams_delta_audio_mode_pins[mode])
+ break;
+
+ if (mode >= ARRAY_SIZE(ams_delta_audio_mode))
+ return -EINVAL;
+
+ ucontrol->value.enumerated.item[0] = mode;
+
+ return 0;
+}
+
+static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum,
+ ams_delta_audio_mode);
+
+static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
+ SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum,
+ ams_delta_get_audio_mode, ams_delta_set_audio_mode),
+};
+
+/* Hook switch */
+static struct snd_soc_jack ams_delta_hook_switch;
+static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = {
+ {
+ .name = "hook_switch",
+ .report = SND_JACK_HEADSET,
+ .invert = 1,
+ .debounce_time = 150,
+ }
+};
+
+/* After we are able to control the codec over the modem,
+ * the hook switch can be used for dynamic DAPM reconfiguration. */
+static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = {
+ /* Handset */
+ {
+ .pin = "Mouthpiece",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Earpiece",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ /* Handsfree */
+ {
+ .pin = "Microphone",
+ .mask = SND_JACK_MICROPHONE,
+ .invert = 1,
+ },
+ {
+ .pin = "Speaker",
+ .mask = SND_JACK_HEADPHONE,
+ .invert = 1,
+ },
+};
+
+
+/*
+ * Modem line discipline, required for making above controls functional.
+ * Activated from userspace with ldattach, possibly invoked from udev rule.
+ */
+
+/* To actually apply any modem controlled configuration changes to the codec,
+ * we must connect codec DAI pins to the modem for a moment. Be careful not
+ * to interfere with our digital mute function that shares the same hardware. */
+static struct timer_list cx81801_timer;
+static bool cx81801_cmd_pending;
+static bool ams_delta_muted;
+static DEFINE_SPINLOCK(ams_delta_lock);
+static struct gpio_desc *gpiod_modem_codec;
+
+static void cx81801_timeout(struct timer_list *unused)
+{
+ int muted;
+
+ spin_lock(&ams_delta_lock);
+ cx81801_cmd_pending = 0;
+ muted = ams_delta_muted;
+ spin_unlock(&ams_delta_lock);
+
+ /* Reconnect the codec DAI back from the modem to the CPU DAI
+ * only if digital mute still off */
+ if (!muted)
+ gpiod_set_value(gpiod_modem_codec, 0);
+}
+
+/* Line discipline .open() */
+static int cx81801_open(struct tty_struct *tty)
+{
+ int ret;
+
+ if (!cx20442_codec)
+ return -ENODEV;
+
+ /*
+ * Pass the codec structure pointer for use by other ldisc callbacks,
+ * both the card and the codec specific parts.
+ */
+ tty->disc_data = cx20442_codec;
+
+ ret = v253_ops.open(tty);
+
+ if (ret < 0)
+ tty->disc_data = NULL;
+
+ return ret;
+}
+
+/* Line discipline .close() */
+static void cx81801_close(struct tty_struct *tty)
+{
+ struct snd_soc_component *component = tty->disc_data;
+ struct snd_soc_dapm_context *dapm = &component->card->dapm;
+
+ del_timer_sync(&cx81801_timer);
+
+ /* Prevent the hook switch from further changing the DAPM pins */
+ INIT_LIST_HEAD(&ams_delta_hook_switch.pins);
+
+ if (!component)
+ return;
+
+ v253_ops.close(tty);
+
+ /* Revert back to default audio input/output constellation */
+ snd_soc_dapm_mutex_lock(dapm);
+
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece");
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker");
+ snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN");
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+/* Line discipline .hangup() */
+static int cx81801_hangup(struct tty_struct *tty)
+{
+ cx81801_close(tty);
+ return 0;
+}
+
+/* Line discipline .receive_buf() */
+static void cx81801_receive(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct snd_soc_component *component = tty->disc_data;
+ const unsigned char *c;
+ int apply, ret;
+
+ if (!component)
+ return;
+
+ if (!component->card->pop_time) {
+ /* First modem response, complete setup procedure */
+
+ /* Initialize timer used for config pulse generation */
+ timer_setup(&cx81801_timer, cx81801_timeout, 0);
+
+ v253_ops.receive_buf(tty, cp, fp, count);
+
+ /* Link hook switch to DAPM pins */
+ ret = snd_soc_jack_add_pins(&ams_delta_hook_switch,
+ ARRAY_SIZE(ams_delta_hook_switch_pins),
+ ams_delta_hook_switch_pins);
+ if (ret)
+ dev_warn(component->dev,
+ "Failed to link hook switch to DAPM pins, "
+ "will continue with hook switch unlinked.\n");
+
+ return;
+ }
+
+ v253_ops.receive_buf(tty, cp, fp, count);
+
+ for (c = &cp[count - 1]; c >= cp; c--) {
+ if (*c != '\r')
+ continue;
+ /* Complete modem response received, apply config to codec */
+
+ spin_lock_bh(&ams_delta_lock);
+ mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150));
+ apply = !ams_delta_muted && !cx81801_cmd_pending;
+ cx81801_cmd_pending = 1;
+ spin_unlock_bh(&ams_delta_lock);
+
+ /* Apply config pulse by connecting the codec to the modem
+ * if not already done */
+ if (apply)
+ gpiod_set_value(gpiod_modem_codec, 1);
+ break;
+ }
+}
+
+/* Line discipline .write_wakeup() */
+static void cx81801_wakeup(struct tty_struct *tty)
+{
+ v253_ops.write_wakeup(tty);
+}
+
+static struct tty_ldisc_ops cx81801_ops = {
+ .magic = TTY_LDISC_MAGIC,
+ .name = "cx81801",
+ .owner = THIS_MODULE,
+ .open = cx81801_open,
+ .close = cx81801_close,
+ .hangup = cx81801_hangup,
+ .receive_buf = cx81801_receive,
+ .write_wakeup = cx81801_wakeup,
+};
+
+
+/*
+ * Even if not very useful, the sound card can still work without any of the
+ * above functonality activated. You can still control its audio input/output
+ * constellation and speakerphone gain from userspace by issuing AT commands
+ * over the modem port.
+ */
+
+static struct snd_soc_ops ams_delta_ops;
+
+
+/* Digital mute implemented using modem/CPU multiplexer.
+ * Shares hardware with codec config pulse generation */
+static bool ams_delta_muted = 1;
+
+static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ int apply;
+
+ if (ams_delta_muted == mute)
+ return 0;
+
+ spin_lock_bh(&ams_delta_lock);
+ ams_delta_muted = mute;
+ apply = !cx81801_cmd_pending;
+ spin_unlock_bh(&ams_delta_lock);
+
+ if (apply)
+ gpiod_set_value(gpiod_modem_codec, !!mute);
+ return 0;
+}
+
+/* Our codec DAI probably doesn't have its own .ops structure */
+static const struct snd_soc_dai_ops ams_delta_dai_ops = {
+ .digital_mute = ams_delta_digital_mute,
+};
+
+/* Will be used if the codec ever has its own digital_mute function */
+static int ams_delta_startup(struct snd_pcm_substream *substream)
+{
+ return ams_delta_digital_mute(NULL, 0);
+}
+
+static void ams_delta_shutdown(struct snd_pcm_substream *substream)
+{
+ ams_delta_digital_mute(NULL, 1);
+}
+
+
+/*
+ * Card initialization
+ */
+
+static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+ int ret;
+ /* Codec is ready, now add/activate board specific controls */
+
+ /* Store a pointer to the codec structure for tty ldisc use */
+ cx20442_codec = rtd->codec_dai->component;
+
+ /* Add hook switch - can be used to control the codec from userspace
+ * even if line discipline fails */
+ ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET,
+ &ams_delta_hook_switch, NULL, 0);
+ if (ret)
+ dev_warn(card->dev,
+ "Failed to allocate resources for hook switch, "
+ "will continue without one.\n");
+ else {
+ ret = snd_soc_jack_add_gpiods(card->dev, &ams_delta_hook_switch,
+ ARRAY_SIZE(ams_delta_hook_switch_gpios),
+ ams_delta_hook_switch_gpios);
+ if (ret)
+ dev_warn(card->dev,
+ "Failed to set up hook switch GPIO line, "
+ "will continue with hook switch inactive.\n");
+ }
+
+ gpiod_modem_codec = devm_gpiod_get(card->dev, "modem_codec",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod_modem_codec)) {
+ dev_warn(card->dev, "Failed to obtain modem_codec GPIO\n");
+ return 0;
+ }
+
+ /* Set up digital mute if not provided by the codec */
+ if (!codec_dai->driver->ops) {
+ codec_dai->driver->ops = &ams_delta_dai_ops;
+ } else {
+ ams_delta_ops.startup = ams_delta_startup;
+ ams_delta_ops.shutdown = ams_delta_shutdown;
+ }
+
+ /* Register optional line discipline for over the modem control */
+ ret = tty_register_ldisc(N_V253, &cx81801_ops);
+ if (ret) {
+ dev_warn(card->dev,
+ "Failed to register line discipline, "
+ "will continue without any controls.\n");
+ return 0;
+ }
+
+ /* Set up initial pin constellation */
+ snd_soc_dapm_disable_pin(dapm, "Mouthpiece");
+ snd_soc_dapm_disable_pin(dapm, "Speaker");
+ snd_soc_dapm_disable_pin(dapm, "AGCIN");
+ snd_soc_dapm_disable_pin(dapm, "AGCOUT");
+
+ return 0;
+}
+
+/* DAI glue - connects codec <--> CPU */
+static struct snd_soc_dai_link ams_delta_dai_link = {
+ .name = "CX20442",
+ .stream_name = "CX20442",
+ .cpu_dai_name = "omap-mcbsp.1",
+ .codec_dai_name = "cx20442-voice",
+ .init = ams_delta_cx20442_init,
+ .platform_name = "omap-mcbsp.1",
+ .codec_name = "cx20442-codec",
+ .ops = &ams_delta_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+};
+
+/* Audio card driver */
+static struct snd_soc_card ams_delta_audio_card = {
+ .name = "AMS_DELTA",
+ .owner = THIS_MODULE,
+ .dai_link = &ams_delta_dai_link,
+ .num_links = 1,
+
+ .controls = ams_delta_audio_controls,
+ .num_controls = ARRAY_SIZE(ams_delta_audio_controls),
+ .dapm_widgets = ams_delta_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ams_delta_dapm_widgets),
+ .dapm_routes = ams_delta_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ams_delta_audio_map),
+};
+
+/* Module init/exit */
+static int ams_delta_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &ams_delta_audio_card;
+ int ret;
+
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ card->dev = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static int ams_delta_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ if (tty_unregister_ldisc(N_V253) != 0)
+ dev_warn(&pdev->dev,
+ "failed to unregister V253 line discipline\n");
+
+ snd_soc_unregister_card(card);
+ card->dev = NULL;
+ return 0;
+}
+
+#define DRV_NAME "ams-delta-audio"
+
+static struct platform_driver ams_delta_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = ams_delta_probe,
+ .remove = ams_delta_remove,
+};
+
+module_platform_driver(ams_delta_driver);
+
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--- /dev/null
+/*
+ * ASoC driver for TI DAVINCI EVM platform
+ *
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <asm/mach-types.h>
+
+struct snd_soc_card_drvdata_davinci {
+ struct clk *mclk;
+ unsigned sysclk;
+};
+
+static int evm_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct snd_soc_card_drvdata_davinci *drvdata =
+ snd_soc_card_get_drvdata(soc_card);
+
+ if (drvdata->mclk)
+ return clk_prepare_enable(drvdata->mclk);
+
+ return 0;
+}
+
+static void evm_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *soc_card = rtd->card;
+ struct snd_soc_card_drvdata_davinci *drvdata =
+ snd_soc_card_get_drvdata(soc_card);
+
+ if (drvdata->mclk)
+ clk_disable_unprepare(drvdata->mclk);
+}
+
+static int evm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_card *soc_card = rtd->card;
+ int ret = 0;
+ unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *)
+ snd_soc_card_get_drvdata(soc_card))->sysclk;
+
+ /* set the codec system clock */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ /* set the CPU system clock */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct snd_soc_ops evm_ops = {
+ .startup = evm_startup,
+ .shutdown = evm_shutdown,
+ .hw_params = evm_hw_params,
+};
+
+/* davinci-evm machine dapm widgets */
+static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+/* davinci-evm machine audio_mapnections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Headphone connected to HPLOUT, HPROUT */
+ {"Headphone Jack", NULL, "HPLOUT"},
+ {"Headphone Jack", NULL, "HPROUT"},
+
+ /* Line Out connected to LLOUT, RLOUT */
+ {"Line Out", NULL, "LLOUT"},
+ {"Line Out", NULL, "RLOUT"},
+
+ /* Mic connected to (MIC3L | MIC3R) */
+ {"MIC3L", NULL, "Mic Bias"},
+ {"MIC3R", NULL, "Mic Bias"},
+ {"Mic Bias", NULL, "Mic Jack"},
+
+ /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
+ {"LINE1L", NULL, "Line In"},
+ {"LINE2L", NULL, "Line In"},
+ {"LINE1R", NULL, "Line In"},
+ {"LINE2R", NULL, "Line In"},
+};
+
+/* Logic for a aic3x as connected on a davinci-evm */
+static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct device_node *np = card->dev->of_node;
+ int ret;
+
+ /* Add davinci-evm specific widgets */
+ snd_soc_dapm_new_controls(&card->dapm, aic3x_dapm_widgets,
+ ARRAY_SIZE(aic3x_dapm_widgets));
+
+ if (np) {
+ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
+ if (ret)
+ return ret;
+ } else {
+ /* Set up davinci-evm specific audio path audio_map */
+ snd_soc_dapm_add_routes(&card->dapm, audio_map,
+ ARRAY_SIZE(audio_map));
+ }
+
+ /* not connected */
+ snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT");
+ snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM");
+ snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM");
+
+ return 0;
+}
+
+/* davinci-evm digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link dm6446_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcbsp",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-001b",
+ .platform_name = "davinci-mcbsp",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static struct snd_soc_dai_link dm355_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcbsp.1",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-001b",
+ .platform_name = "davinci-mcbsp.1",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static struct snd_soc_dai_link dm365_evm_dai = {
+#ifdef CONFIG_SND_SOC_DM365_AIC3X_CODEC
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcbsp",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .platform_name = "davinci-mcbsp",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+#elif defined(CONFIG_SND_SOC_DM365_VOICE_CODEC)
+ .name = "Voice Codec - CQ93VC",
+ .stream_name = "CQ93",
+ .cpu_dai_name = "davinci-vcif",
+ .codec_dai_name = "cq93vc-hifi",
+ .codec_name = "cq93vc-codec",
+ .platform_name = "davinci-vcif",
+#endif
+};
+
+static struct snd_soc_dai_link dm6467_evm_dai[] = {
+ {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name= "davinci-mcasp.0",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .platform_name = "davinci-mcasp.0",
+ .codec_name = "tlv320aic3x-codec.0-001a",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+ },
+ {
+ .name = "McASP",
+ .stream_name = "spdif",
+ .cpu_dai_name= "davinci-mcasp.1",
+ .codec_dai_name = "dit-hifi",
+ .codec_name = "spdif_dit",
+ .platform_name = "davinci-mcasp.1",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+ },
+};
+
+static struct snd_soc_dai_link da830_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name = "davinci-mcasp.1",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .platform_name = "davinci-mcasp.1",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static struct snd_soc_dai_link da850_evm_dai = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .cpu_dai_name= "davinci-mcasp.0",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .platform_name = "davinci-mcasp.0",
+ .init = evm_aic3x_init,
+ .ops = &evm_ops,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+/* davinci dm6446 evm audio machine driver */
+/*
+ * ASP0 in DM6446 EVM is clocked by U55, as configured by
+ * board-dm644x-evm.c using GPIOs from U18. There are six
+ * options; here we "know" we use a 48 KHz sample rate.
+ */
+static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = {
+ .sysclk = 12288000,
+};
+
+static struct snd_soc_card dm6446_snd_soc_card_evm = {
+ .name = "DaVinci DM6446 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &dm6446_evm_dai,
+ .num_links = 1,
+ .drvdata = &dm6446_snd_soc_card_drvdata,
+};
+
+/* davinci dm355 evm audio machine driver */
+/* ASP1 on DM355 EVM is clocked by an external oscillator */
+static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = {
+ .sysclk = 27000000,
+};
+
+static struct snd_soc_card dm355_snd_soc_card_evm = {
+ .name = "DaVinci DM355 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &dm355_evm_dai,
+ .num_links = 1,
+ .drvdata = &dm355_snd_soc_card_drvdata,
+};
+
+/* davinci dm365 evm audio machine driver */
+static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = {
+ .sysclk = 27000000,
+};
+
+static struct snd_soc_card dm365_snd_soc_card_evm = {
+ .name = "DaVinci DM365 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &dm365_evm_dai,
+ .num_links = 1,
+ .drvdata = &dm365_snd_soc_card_drvdata,
+};
+
+/* davinci dm6467 evm audio machine driver */
+static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = {
+ .sysclk = 27000000,
+};
+
+static struct snd_soc_card dm6467_snd_soc_card_evm = {
+ .name = "DaVinci DM6467 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = dm6467_evm_dai,
+ .num_links = ARRAY_SIZE(dm6467_evm_dai),
+ .drvdata = &dm6467_snd_soc_card_drvdata,
+};
+
+static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = {
+ .sysclk = 24576000,
+};
+
+static struct snd_soc_card da830_snd_soc_card = {
+ .name = "DA830/OMAP-L137 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &da830_evm_dai,
+ .num_links = 1,
+ .drvdata = &da830_snd_soc_card_drvdata,
+};
+
+static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = {
+ .sysclk = 24576000,
+};
+
+static struct snd_soc_card da850_snd_soc_card = {
+ .name = "DA850/OMAP-L138 EVM",
+ .owner = THIS_MODULE,
+ .dai_link = &da850_evm_dai,
+ .num_links = 1,
+ .drvdata = &da850_snd_soc_card_drvdata,
+};
+
+#if defined(CONFIG_OF)
+
+/*
+ * The struct is used as place holder. It will be completely
+ * filled with data from dt node.
+ */
+static struct snd_soc_dai_link evm_dai_tlv320aic3x = {
+ .name = "TLV320AIC3X",
+ .stream_name = "AIC3X",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .ops = &evm_ops,
+ .init = evm_aic3x_init,
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM |
+ SND_SOC_DAIFMT_IB_NF,
+};
+
+static const struct of_device_id davinci_evm_dt_ids[] = {
+ {
+ .compatible = "ti,da830-evm-audio",
+ .data = (void *) &evm_dai_tlv320aic3x,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids);
+
+/* davinci evm audio machine driver */
+static struct snd_soc_card evm_soc_card = {
+ .owner = THIS_MODULE,
+ .num_links = 1,
+};
+
+static int davinci_evm_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ struct snd_soc_dai_link *dai;
+ struct snd_soc_card_drvdata_davinci *drvdata = NULL;
+ struct clk *mclk;
+ int ret = 0;
+
+ match = of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+
+ dai = (struct snd_soc_dai_link *) match->data;
+
+ evm_soc_card.dai_link = dai;
+
+ dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0);
+ if (!dai->codec_of_node)
+ return -EINVAL;
+
+ dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0);
+ if (!dai->cpu_of_node)
+ return -EINVAL;
+
+ dai->platform_of_node = dai->cpu_of_node;
+
+ evm_soc_card.dev = &pdev->dev;
+ ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model");
+ if (ret)
+ return ret;
+
+ mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (PTR_ERR(mclk) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (IS_ERR(mclk)) {
+ dev_dbg(&pdev->dev, "mclk not found.\n");
+ mclk = NULL;
+ }
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata)
+ return -ENOMEM;
+
+ drvdata->mclk = mclk;
+
+ ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk);
+
+ if (ret < 0) {
+ if (!drvdata->mclk) {
+ dev_err(&pdev->dev,
+ "No clock or clock rate defined.\n");
+ return -EINVAL;
+ }
+ drvdata->sysclk = clk_get_rate(drvdata->mclk);
+ } else if (drvdata->mclk) {
+ unsigned int requestd_rate = drvdata->sysclk;
+ clk_set_rate(drvdata->mclk, drvdata->sysclk);
+ drvdata->sysclk = clk_get_rate(drvdata->mclk);
+ if (drvdata->sysclk != requestd_rate)
+ dev_warn(&pdev->dev,
+ "Could not get requested rate %u using %u.\n",
+ requestd_rate, drvdata->sysclk);
+ }
+
+ snd_soc_card_set_drvdata(&evm_soc_card, drvdata);
+ ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card);
+
+ if (ret)
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+
+ return ret;
+}
+
+static struct platform_driver davinci_evm_driver = {
+ .probe = davinci_evm_probe,
+ .driver = {
+ .name = "davinci_evm",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = of_match_ptr(davinci_evm_dt_ids),
+ },
+};
+#endif
+
+static struct platform_device *evm_snd_device;
+
+static int __init evm_init(void)
+{
+ struct snd_soc_card *evm_snd_dev_data;
+ int index;
+ int ret;
+
+ /*
+ * If dtb is there, the devices will be created dynamically.
+ * Only register platfrom driver structure.
+ */
+#if defined(CONFIG_OF)
+ if (of_have_populated_dt())
+ return platform_driver_register(&davinci_evm_driver);
+#endif
+
+ if (machine_is_davinci_evm()) {
+ evm_snd_dev_data = &dm6446_snd_soc_card_evm;
+ index = 0;
+ } else if (machine_is_davinci_dm355_evm()) {
+ evm_snd_dev_data = &dm355_snd_soc_card_evm;
+ index = 1;
+ } else if (machine_is_davinci_dm365_evm()) {
+ evm_snd_dev_data = &dm365_snd_soc_card_evm;
+ index = 0;
+ } else if (machine_is_davinci_dm6467_evm()) {
+ evm_snd_dev_data = &dm6467_snd_soc_card_evm;
+ index = 0;
+ } else if (machine_is_davinci_da830_evm()) {
+ evm_snd_dev_data = &da830_snd_soc_card;
+ index = 1;
+ } else if (machine_is_davinci_da850_evm()) {
+ evm_snd_dev_data = &da850_snd_soc_card;
+ index = 0;
+ } else
+ return -EINVAL;
+
+ evm_snd_device = platform_device_alloc("soc-audio", index);
+ if (!evm_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
+ ret = platform_device_add(evm_snd_device);
+ if (ret)
+ platform_device_put(evm_snd_device);
+
+ return ret;
+}
+
+static void __exit evm_exit(void)
+{
+#if defined(CONFIG_OF)
+ if (of_have_populated_dt()) {
+ platform_driver_unregister(&davinci_evm_driver);
+ return;
+ }
+#endif
+
+ platform_device_unregister(evm_snd_device);
+}
+
+module_init(evm_init);
+module_exit(evm_exit);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
+ *
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * DT support (c) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
+ * based on davinci-mcasp.c DT support
+ *
+ * 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.
+ *
+ * TODO:
+ * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/platform_data/davinci_asp.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "edma-pcm.h"
+#include "davinci-i2s.h"
+
+#define DRV_NAME "davinci-i2s"
+
+/*
+ * NOTE: terminology here is confusing.
+ *
+ * - This driver supports the "Audio Serial Port" (ASP),
+ * found on dm6446, dm355, and other DaVinci chips.
+ *
+ * - But it labels it a "Multi-channel Buffered Serial Port"
+ * (McBSP) as on older chips like the dm642 ... which was
+ * backward-compatible, possibly explaining that confusion.
+ *
+ * - OMAP chips have a controller called McBSP, which is
+ * incompatible with the DaVinci flavor of McBSP.
+ *
+ * - Newer DaVinci chips have a controller called McASP,
+ * incompatible with ASP and with either McBSP.
+ *
+ * In short: this uses ASP to implement I2S, not McBSP.
+ * And it won't be the only DaVinci implemention of I2S.
+ */
+#define DAVINCI_MCBSP_DRR_REG 0x00
+#define DAVINCI_MCBSP_DXR_REG 0x04
+#define DAVINCI_MCBSP_SPCR_REG 0x08
+#define DAVINCI_MCBSP_RCR_REG 0x0c
+#define DAVINCI_MCBSP_XCR_REG 0x10
+#define DAVINCI_MCBSP_SRGR_REG 0x14
+#define DAVINCI_MCBSP_PCR_REG 0x24
+
+#define DAVINCI_MCBSP_SPCR_RRST (1 << 0)
+#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4)
+#define DAVINCI_MCBSP_SPCR_XRST (1 << 16)
+#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20)
+#define DAVINCI_MCBSP_SPCR_GRST (1 << 22)
+#define DAVINCI_MCBSP_SPCR_FRST (1 << 23)
+#define DAVINCI_MCBSP_SPCR_FREE (1 << 25)
+
+#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5)
+#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8)
+#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16)
+#define DAVINCI_MCBSP_RCR_RFIG (1 << 18)
+#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21)
+#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24)
+#define DAVINCI_MCBSP_RCR_RPHASE BIT(31)
+
+#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5)
+#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8)
+#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16)
+#define DAVINCI_MCBSP_XCR_XFIG (1 << 18)
+#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21)
+#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24)
+#define DAVINCI_MCBSP_XCR_XPHASE BIT(31)
+
+#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8)
+#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16)
+#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28)
+#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29)
+
+#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0)
+#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1)
+#define DAVINCI_MCBSP_PCR_FSRP (1 << 2)
+#define DAVINCI_MCBSP_PCR_FSXP (1 << 3)
+#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7)
+#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8)
+#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9)
+#define DAVINCI_MCBSP_PCR_FSRM (1 << 10)
+#define DAVINCI_MCBSP_PCR_FSXM (1 << 11)
+
+enum {
+ DAVINCI_MCBSP_WORD_8 = 0,
+ DAVINCI_MCBSP_WORD_12,
+ DAVINCI_MCBSP_WORD_16,
+ DAVINCI_MCBSP_WORD_20,
+ DAVINCI_MCBSP_WORD_24,
+ DAVINCI_MCBSP_WORD_32,
+};
+
+static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = 1,
+ [SNDRV_PCM_FORMAT_S16_LE] = 2,
+ [SNDRV_PCM_FORMAT_S32_LE] = 4,
+};
+
+static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
+ [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
+ [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
+};
+
+static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = {
+ [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE,
+ [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE,
+};
+
+struct davinci_mcbsp_dev {
+ struct device *dev;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ int dma_request[2];
+ void __iomem *base;
+#define MOD_DSP_A 0
+#define MOD_DSP_B 1
+ int mode;
+ u32 pcr;
+ struct clk *clk;
+ /*
+ * Combining both channels into 1 element will at least double the
+ * amount of time between servicing the dma channel, increase
+ * effiency, and reduce the chance of overrun/underrun. But,
+ * it will result in the left & right channels being swapped.
+ *
+ * If relabeling the left and right channels is not possible,
+ * you may want to let the codec know to swap them back.
+ *
+ * It may allow x10 the amount of time to service dma requests,
+ * if the codec is master and is using an unnecessarily fast bit clock
+ * (ie. tlvaic23b), independent of the sample rate. So, having an
+ * entire frame at once means it can be serviced at the sample rate
+ * instead of the bit clock rate.
+ *
+ * In the now unlikely case that an underrun still
+ * occurs, both the left and right samples will be repeated
+ * so that no pops are heard, and the left and right channels
+ * won't end up being swapped because of the underrun.
+ */
+ unsigned enable_channel_combine:1;
+
+ unsigned int fmt;
+ int clk_div;
+ int clk_input_pin;
+ bool i2s_accurate_sck;
+};
+
+static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
+ int reg, u32 val)
+{
+ __raw_writel(val, dev->base + reg);
+}
+
+static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg)
+{
+ return __raw_readl(dev->base + reg);
+}
+
+static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
+{
+ u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP;
+ /* The clock needs to toggle to complete reset.
+ * So, fake it by toggling the clk polarity.
+ */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
+}
+
+static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME);
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ u32 spcr;
+ u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST;
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ if (spcr & mask) {
+ /* start off disabled */
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
+ spcr & ~mask);
+ toggle_clock(dev, playback);
+ }
+ if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
+ DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) {
+ /* Start the sample generator */
+ spcr |= DAVINCI_MCBSP_SPCR_GRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ }
+
+ if (playback) {
+ /* Stop the DMA to avoid data loss */
+ /* while the transmitter is out of reset to handle XSYNCERR */
+ if (component->driver->ops->trigger) {
+ int ret = component->driver->ops->trigger(substream,
+ SNDRV_PCM_TRIGGER_STOP);
+ if (ret < 0)
+ printk(KERN_DEBUG "Playback DMA stop failed\n");
+ }
+
+ /* Enable the transmitter */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr |= DAVINCI_MCBSP_SPCR_XRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+
+ /* wait for any unexpected frame sync error to occur */
+ udelay(100);
+
+ /* Disable the transmitter to clear any outstanding XSYNCERR */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ toggle_clock(dev, playback);
+
+ /* Restart the DMA */
+ if (component->driver->ops->trigger) {
+ int ret = component->driver->ops->trigger(substream,
+ SNDRV_PCM_TRIGGER_START);
+ if (ret < 0)
+ printk(KERN_DEBUG "Playback DMA start failed\n");
+ }
+ }
+
+ /* Enable transmitter or receiver */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr |= mask;
+
+ if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) {
+ /* Start frame sync */
+ spcr |= DAVINCI_MCBSP_SPCR_FRST;
+ }
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+}
+
+static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
+{
+ u32 spcr;
+
+ /* Reset transmitter/receiver and sample rate/frame sync generators */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
+ spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ toggle_clock(dev, playback);
+}
+
+#define DEFAULT_BITPERSAMPLE 16
+
+static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned int pcr;
+ unsigned int srgr;
+ bool inv_fs = false;
+ /* Attention srgr is updated by hw_params! */
+ srgr = DAVINCI_MCBSP_SRGR_FSGM |
+ DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
+ DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
+
+ dev->fmt = fmt;
+ /* set master/slave audio interface */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* cpu is master */
+ pcr = DAVINCI_MCBSP_PCR_FSXM |
+ DAVINCI_MCBSP_PCR_FSRM |
+ DAVINCI_MCBSP_PCR_CLKXM |
+ DAVINCI_MCBSP_PCR_CLKRM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
+ /*
+ * Selection of the clock input pin that is the
+ * input for the Sample Rate Generator.
+ * McBSP FSR and FSX are driven by the Sample Rate
+ * Generator.
+ */
+ switch (dev->clk_input_pin) {
+ case MCBSP_CLKS:
+ pcr |= DAVINCI_MCBSP_PCR_CLKXM |
+ DAVINCI_MCBSP_PCR_CLKRM;
+ break;
+ case MCBSP_CLKR:
+ pcr |= DAVINCI_MCBSP_PCR_SCLKME;
+ break;
+ default:
+ dev_err(dev->dev, "bad clk_input_pin\n");
+ return -EINVAL;
+ }
+
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* codec is master */
+ pcr = 0;
+ break;
+ default:
+ printk(KERN_ERR "%s:bad master\n", __func__);
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Davinci doesn't support TRUE I2S, but some codecs will have
+ * the left and right channels contiguous. This allows
+ * dsp_a mode to be used with an inverted normal frame clk.
+ * If your codec is master and does not have contiguous
+ * channels, then you will have sound on only one channel.
+ * Try using a different mode, or codec as slave.
+ *
+ * The TLV320AIC33 is an example of a codec where this works.
+ * It has a variable bit clock frequency allowing it to have
+ * valid data on every bit clock.
+ *
+ * The TLV320AIC23 is an example of a codec where this does not
+ * work. It has a fixed bit clock frequency with progressively
+ * more empty bit clock slots between channels as the sample
+ * rate is lowered.
+ */
+ inv_fs = true;
+ /* fall through */
+ case SND_SOC_DAIFMT_DSP_A:
+ dev->mode = MOD_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ dev->mode = MOD_DSP_B;
+ break;
+ default:
+ printk(KERN_ERR "%s:bad format\n", __func__);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /* CLKRP Receive clock polarity,
+ * 1 - sampled on rising edge of CLKR
+ * valid on rising edge
+ * CLKXP Transmit clock polarity,
+ * 1 - clocked on falling edge of CLKX
+ * valid on rising edge
+ * FSRP Receive frame sync pol, 0 - active high
+ * FSXP Transmit frame sync pol, 0 - active high
+ */
+ pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* CLKRP Receive clock polarity,
+ * 0 - sampled on falling edge of CLKR
+ * valid on falling edge
+ * CLKXP Transmit clock polarity,
+ * 0 - clocked on rising edge of CLKX
+ * valid on falling edge
+ * FSRP Receive frame sync pol, 1 - active low
+ * FSXP Transmit frame sync pol, 1 - active low
+ */
+ pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* CLKRP Receive clock polarity,
+ * 1 - sampled on rising edge of CLKR
+ * valid on rising edge
+ * CLKXP Transmit clock polarity,
+ * 1 - clocked on falling edge of CLKX
+ * valid on rising edge
+ * FSRP Receive frame sync pol, 1 - active low
+ * FSXP Transmit frame sync pol, 1 - active low
+ */
+ pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP |
+ DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* CLKRP Receive clock polarity,
+ * 0 - sampled on falling edge of CLKR
+ * valid on falling edge
+ * CLKXP Transmit clock polarity,
+ * 0 - clocked on rising edge of CLKX
+ * valid on falling edge
+ * FSRP Receive frame sync pol, 0 - active high
+ * FSXP Transmit frame sync pol, 0 - active high
+ */
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (inv_fs == true)
+ pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP);
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+ dev->pcr = pcr;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
+ return 0;
+}
+
+static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (div_id != DAVINCI_MCBSP_CLKGDV)
+ return -ENODEV;
+
+ dev->clk_div = div;
+ return 0;
+}
+
+static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct snd_interval *i = NULL;
+ int mcbsp_word_length, master;
+ unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
+ u32 spcr;
+ snd_pcm_format_t fmt;
+ unsigned element_cnt = 1;
+
+ /* general line settings */
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ } else {
+ spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+ }
+
+ master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ fmt = params_format(params);
+ mcbsp_word_length = asp_word_length[fmt];
+
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ freq = clk_get_rate(dev->clk);
+ srgr = DAVINCI_MCBSP_SRGR_FSGM |
+ DAVINCI_MCBSP_SRGR_CLKSM;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
+ 8 - 1);
+ if (dev->i2s_accurate_sck) {
+ clk_div = 256;
+ do {
+ framesize = (freq / (--clk_div)) /
+ params->rate_num *
+ params->rate_den;
+ } while (((framesize < 33) || (framesize > 4095)) &&
+ (clk_div));
+ clk_div--;
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1);
+ } else {
+ /* symmetric waveforms */
+ clk_div = freq / (mcbsp_word_length * 16) /
+ params->rate_num * params->rate_den;
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length *
+ 16 - 1);
+ }
+ clk_div &= 0xFF;
+ srgr |= clk_div;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ srgr = DAVINCI_MCBSP_SRGR_FSGM;
+ clk_div = dev->clk_div - 1;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1);
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1);
+ clk_div &= 0xFF;
+ srgr |= clk_div;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* Clock and frame sync given from external sources */
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
+ srgr = DAVINCI_MCBSP_SRGR_FSGM;
+ srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
+ pr_debug("%s - %d FWID set: re-read srgr = %X\n",
+ __func__, __LINE__, snd_interval_value(i) - 1);
+
+ i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
+ srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+
+ rcr = DAVINCI_MCBSP_RCR_RFIG;
+ xcr = DAVINCI_MCBSP_XCR_XFIG;
+ if (dev->mode == MOD_DSP_B) {
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
+ } else {
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+ }
+ /* Determine xfer data type */
+ fmt = params_format(params);
+ if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
+ printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
+ return -EINVAL;
+ }
+
+ if (params_channels(params) == 2) {
+ element_cnt = 2;
+ if (double_fmt[fmt] && dev->enable_channel_combine) {
+ element_cnt = 1;
+ fmt = double_fmt[fmt];
+ }
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0);
+ rcr |= DAVINCI_MCBSP_RCR_RPHASE;
+ xcr |= DAVINCI_MCBSP_XCR_XPHASE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ mcbsp_word_length = asp_word_length[fmt];
+
+ switch (master) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBM_CFS:
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
+ xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+ DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
+ else
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+
+ pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr);
+ pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr);
+ pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr);
+ return 0;
+}
+
+static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ davinci_mcbsp_stop(dev, playback);
+ return 0;
+}
+
+static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_mcbsp_start(dev, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_mcbsp_stop(dev, playback);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ davinci_mcbsp_stop(dev, playback);
+}
+
+#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
+
+static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
+ .shutdown = davinci_i2s_shutdown,
+ .prepare = davinci_i2s_prepare,
+ .trigger = davinci_i2s_trigger,
+ .hw_params = davinci_i2s_hw_params,
+ .set_fmt = davinci_i2s_set_dai_fmt,
+ .set_clkdiv = davinci_i2s_dai_set_clkdiv,
+
+};
+
+static int davinci_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver davinci_i2s_dai = {
+ .probe = davinci_i2s_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = DAVINCI_I2S_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &davinci_i2s_dai_ops,
+
+};
+
+static const struct snd_soc_component_driver davinci_i2s_component = {
+ .name = DRV_NAME,
+};
+
+static int davinci_i2s_probe(struct platform_device *pdev)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ struct davinci_mcbsp_dev *dev;
+ struct resource *mem, *res;
+ void __iomem *io_base;
+ int *dma;
+ int ret;
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ if (!mem) {
+ dev_warn(&pdev->dev,
+ "\"mpu\" mem resource not found, using index 0\n");
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ }
+
+ io_base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(io_base))
+ return PTR_ERR(io_base);
+
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev),
+ GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->base = io_base;
+
+ /* setup DMA, first TX, then RX */
+ dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (res) {
+ dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
+ *dma = res->start;
+ dma_data->filter_data = dma;
+ } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ dma_data->filter_data = "tx";
+ } else {
+ dev_err(&pdev->dev, "Missing DMA tx resource\n");
+ return -ENODEV;
+ }
+
+ dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (res) {
+ dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE];
+ *dma = res->start;
+ dma_data->filter_data = dma;
+ } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+ dma_data->filter_data = "rx";
+ } else {
+ dev_err(&pdev->dev, "Missing DMA rx resource\n");
+ return -ENODEV;
+ }
+
+ dev->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return -ENODEV;
+ clk_enable(dev->clk);
+
+ dev->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, dev);
+
+ ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component,
+ &davinci_i2s_dai, 1);
+ if (ret != 0)
+ goto err_release_clk;
+
+ ret = edma_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err_unregister_component;
+ }
+
+ return 0;
+
+err_unregister_component:
+ snd_soc_unregister_component(&pdev->dev);
+err_release_clk:
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ return ret;
+}
+
+static int davinci_i2s_remove(struct platform_device *pdev)
+{
+ struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ clk_disable(dev->clk);
+ clk_put(dev->clk);
+ dev->clk = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id davinci_i2s_match[] = {
+ { .compatible = "ti,da850-mcbsp" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, davinci_i2s_match);
+
+static struct platform_driver davinci_mcbsp_driver = {
+ .probe = davinci_i2s_probe,
+ .remove = davinci_i2s_remove,
+ .driver = {
+ .name = "davinci-mcbsp",
+ .of_match_table = of_match_ptr(davinci_i2s_match),
+ },
+};
+
+module_platform_driver(davinci_mcbsp_driver);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor
+ *
+ * Author: Vladimir Barinov, <vbarinov@embeddedalley.com>
+ * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * 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 _DAVINCI_I2S_H
+#define _DAVINCI_I2S_H
+
+/* McBSP dividers */
+enum davinci_mcbsp_div {
+ DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */
+};
+
+#endif
--- /dev/null
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ * Suresh Rajashekara <suresh.r@ti.com>
+ * Steve Chen <schen@.mvista.com>
+ *
+ * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright: (C) 2009 Texas Instruments, India
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/davinci_asp.h>
+#include <linux/math64.h>
+#include <linux/bitmap.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "edma-pcm.h"
+#include "sdma-pcm.h"
+#include "davinci-mcasp.h"
+
+#define MCASP_MAX_AFIFO_DEPTH 64
+
+static u32 context_regs[] = {
+ DAVINCI_MCASP_TXFMCTL_REG,
+ DAVINCI_MCASP_RXFMCTL_REG,
+ DAVINCI_MCASP_TXFMT_REG,
+ DAVINCI_MCASP_RXFMT_REG,
+ DAVINCI_MCASP_ACLKXCTL_REG,
+ DAVINCI_MCASP_ACLKRCTL_REG,
+ DAVINCI_MCASP_AHCLKXCTL_REG,
+ DAVINCI_MCASP_AHCLKRCTL_REG,
+ DAVINCI_MCASP_PDIR_REG,
+ DAVINCI_MCASP_RXMASK_REG,
+ DAVINCI_MCASP_TXMASK_REG,
+ DAVINCI_MCASP_RXTDM_REG,
+ DAVINCI_MCASP_TXTDM_REG,
+};
+
+struct davinci_mcasp_context {
+ u32 config_regs[ARRAY_SIZE(context_regs)];
+ u32 afifo_regs[2]; /* for read/write fifo control registers */
+ u32 *xrsr_regs; /* for serializer configuration */
+ bool pm_state;
+};
+
+struct davinci_mcasp_ruledata {
+ struct davinci_mcasp *mcasp;
+ int serializers;
+};
+
+struct davinci_mcasp {
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ void __iomem *base;
+ u32 fifo_base;
+ struct device *dev;
+ struct snd_pcm_substream *substreams[2];
+ unsigned int dai_fmt;
+
+ /* McASP specific data */
+ int tdm_slots;
+ u32 tdm_mask[2];
+ int slot_width;
+ u8 op_mode;
+ u8 dismod;
+ u8 num_serializer;
+ u8 *serial_dir;
+ u8 version;
+ u8 bclk_div;
+ int streams;
+ u32 irq_request[2];
+ int dma_request[2];
+
+ int sysclk_freq;
+ bool bclk_master;
+
+ unsigned long pdir; /* Pin direction bitfield */
+
+ /* McASP FIFO related */
+ u8 txnumevt;
+ u8 rxnumevt;
+
+ bool dat_port;
+
+ /* Used for comstraint setting on the second stream */
+ u32 channels;
+
+#ifdef CONFIG_PM_SLEEP
+ struct davinci_mcasp_context context;
+#endif
+
+ struct davinci_mcasp_ruledata ruledata[2];
+ struct snd_pcm_hw_constraint_list chconstr[2];
+};
+
+static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
+{
+ void __iomem *reg = mcasp->base + offset;
+ __raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
+{
+ void __iomem *reg = mcasp->base + offset;
+ __raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val, u32 mask)
+{
+ void __iomem *reg = mcasp->base + offset;
+ __raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset,
+ u32 val)
+{
+ __raw_writel(val, mcasp->base + offset);
+}
+
+static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset)
+{
+ return (u32)__raw_readl(mcasp->base + offset);
+}
+
+static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val)
+{
+ int i = 0;
+
+ mcasp_set_bits(mcasp, ctl_reg, val);
+
+ /* programming GBLCTL needs to read back from GBLCTL and verfiy */
+ /* loop count is to avoid the lock-up */
+ for (i = 0; i < 1000; i++) {
+ if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val)
+ break;
+ }
+
+ if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val))
+ printk(KERN_ERR "GBLCTL write error\n");
+}
+
+static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
+{
+ u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG);
+ u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG);
+
+ return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE;
+}
+
+static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable)
+{
+ u32 bit = PIN_BIT_AMUTE;
+
+ for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) {
+ if (enable)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ }
+}
+
+static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
+{
+ u32 bit;
+
+ for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AFSR) {
+ if (enable)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ }
+}
+
+static void mcasp_start_rx(struct davinci_mcasp *mcasp)
+{
+ if (mcasp->rxnumevt) { /* enable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ /* Start clocks */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
+ /*
+ * When ASYNC == 0 the transmit and receive sections operate
+ * synchronously from the transmit clock and frame sync. We need to make
+ * sure that the TX signlas are enabled when starting reception.
+ */
+ if (mcasp_is_synchronous(mcasp)) {
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ }
+
+ /* Activate serializer(s) */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
+ /* Release RX state machine */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ /* Release Frame Sync generator */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+ /* enable receive IRQs */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
+}
+
+static void mcasp_start_tx(struct davinci_mcasp *mcasp)
+{
+ u32 cnt;
+
+ if (mcasp->txnumevt) { /* enable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ /* Start clocks */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ mcasp_set_clk_pdir(mcasp, true);
+
+ /* Activate serializer(s) */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+
+ /* wait for XDATA to be cleared */
+ cnt = 0;
+ while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) &&
+ (cnt < 100000))
+ cnt++;
+
+ mcasp_set_axr_pdir(mcasp, true);
+
+ /* Release TX state machine */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+ /* Release Frame Sync generator */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+ /* enable transmit IRQs */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
+}
+
+static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
+{
+ mcasp->streams++;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcasp_start_tx(mcasp);
+ else
+ mcasp_start_rx(mcasp);
+}
+
+static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
+{
+ /* disable IRQ sources */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
+
+ /*
+ * In synchronous mode stop the TX clocks if no other stream is
+ * running
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams) {
+ mcasp_set_clk_pdir(mcasp, false);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
+ }
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+ if (mcasp->rxnumevt) { /* disable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
+}
+
+static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
+{
+ u32 val = 0;
+
+ /* disable IRQ sources */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
+
+ /*
+ * In synchronous mode keep TX clocks running if the capture stream is
+ * still running.
+ */
+ if (mcasp_is_synchronous(mcasp) && mcasp->streams)
+ val = TXHCLKRST | TXCLKRST | TXFSRST;
+ else
+ mcasp_set_clk_pdir(mcasp, false);
+
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+ if (mcasp->txnumevt) { /* disable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ mcasp_set_axr_pdir(mcasp, false);
+}
+
+static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
+{
+ mcasp->streams--;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mcasp_stop_tx(mcasp);
+ else
+ mcasp_stop_rx(mcasp);
+}
+
+static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ struct snd_pcm_substream *substream;
+ u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK];
+ u32 handled_mask = 0;
+ u32 stat;
+
+ stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
+ if (stat & XUNDRN & irq_mask) {
+ dev_warn(mcasp->dev, "Transmit buffer underflow\n");
+ handled_mask |= XUNDRN;
+
+ substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (!handled_mask)
+ dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n",
+ stat);
+
+ if (stat & XRERR)
+ handled_mask |= XRERR;
+
+ /* Ack the handled event only */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask);
+
+ return IRQ_RETVAL(handled_mask);
+}
+
+static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ struct snd_pcm_substream *substream;
+ u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE];
+ u32 handled_mask = 0;
+ u32 stat;
+
+ stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
+ if (stat & ROVRN & irq_mask) {
+ dev_warn(mcasp->dev, "Receive buffer overflow\n");
+ handled_mask |= ROVRN;
+
+ substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (!handled_mask)
+ dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n",
+ stat);
+
+ if (stat & XRERR)
+ handled_mask |= XRERR;
+
+ /* Ack the handled event only */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask);
+
+ return IRQ_RETVAL(handled_mask);
+}
+
+static irqreturn_t davinci_mcasp_common_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK])
+ ret = davinci_mcasp_tx_irq_handler(irq, data);
+
+ if (mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE])
+ ret |= davinci_mcasp_rx_irq_handler(irq, data);
+
+ return ret;
+}
+
+static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+ u32 data_delay;
+ bool fs_pol_rising;
+ bool inv_fs = false;
+
+ if (!fmt)
+ return 0;
+
+ pm_runtime_get_sync(mcasp->dev);
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* 1st data bit occur one ACLK cycle after the frame sync */
+ data_delay = 1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ case SND_SOC_DAIFMT_AC97:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* No delay after FS */
+ data_delay = 0;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ /* configure a full-word SYNC pulse (LRCLK) */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* 1st data bit occur one ACLK cycle after the frame sync */
+ data_delay = 1;
+ /* FS need to be inverted */
+ inv_fs = true;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* configure a full-word SYNC pulse (LRCLK) */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+ /* No delay after FS */
+ data_delay = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay),
+ FSXDLY(3));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay),
+ FSRDLY(3));
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* codec is clock and frame slave */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ /* BCLK */
+ set_bit(PIN_BIT_ACLKX, &mcasp->pdir);
+ set_bit(PIN_BIT_ACLKR, &mcasp->pdir);
+ /* Frame Sync */
+ set_bit(PIN_BIT_AFSX, &mcasp->pdir);
+ set_bit(PIN_BIT_AFSR, &mcasp->pdir);
+
+ mcasp->bclk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFM:
+ /* codec is clock slave and frame master */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ /* BCLK */
+ set_bit(PIN_BIT_ACLKX, &mcasp->pdir);
+ set_bit(PIN_BIT_ACLKR, &mcasp->pdir);
+ /* Frame Sync */
+ clear_bit(PIN_BIT_AFSX, &mcasp->pdir);
+ clear_bit(PIN_BIT_AFSR, &mcasp->pdir);
+
+ mcasp->bclk_master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /* codec is clock master and frame slave */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ /* BCLK */
+ clear_bit(PIN_BIT_ACLKX, &mcasp->pdir);
+ clear_bit(PIN_BIT_ACLKR, &mcasp->pdir);
+ /* Frame Sync */
+ set_bit(PIN_BIT_AFSX, &mcasp->pdir);
+ set_bit(PIN_BIT_AFSR, &mcasp->pdir);
+
+ mcasp->bclk_master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* codec is clock and frame master */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+ /* BCLK */
+ clear_bit(PIN_BIT_ACLKX, &mcasp->pdir);
+ clear_bit(PIN_BIT_ACLKR, &mcasp->pdir);
+ /* Frame Sync */
+ clear_bit(PIN_BIT_AFSX, &mcasp->pdir);
+ clear_bit(PIN_BIT_AFSR, &mcasp->pdir);
+
+ mcasp->bclk_master = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = true;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = false;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+ fs_pol_rising = true;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (inv_fs)
+ fs_pol_rising = !fs_pol_rising;
+
+ if (fs_pol_rising) {
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ } else {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+ }
+
+ mcasp->dai_fmt = fmt;
+out:
+ pm_runtime_put(mcasp->dev);
+ return ret;
+}
+
+static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
+ int div, bool explicit)
+{
+ pm_runtime_get_sync(mcasp->dev);
+ switch (div_id) {
+ case MCASP_CLKDIV_AUXCLK: /* MCLK divider */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
+ break;
+
+ case MCASP_CLKDIV_BCLK: /* BCLK divider */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
+ ACLKXDIV(div - 1), ACLKXDIV_MASK);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
+ ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ if (explicit)
+ mcasp->bclk_div = div;
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO:
+ /*
+ * BCLK/LRCLK ratio descries how many bit-clock cycles
+ * fit into one frame. The clock ratio is given for a
+ * full period of data (for I2S format both left and
+ * right channels), so it has to be divided by number
+ * of tdm-slots (for I2S - divided by 2).
+ * Instead of storing this ratio, we calculate a new
+ * tdm_slot width by dividing the the ratio by the
+ * number of configured tdm slots.
+ */
+ mcasp->slot_width = div / mcasp->tdm_slots;
+ if (div % mcasp->tdm_slots)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+ __func__, div, mcasp->tdm_slots);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ pm_runtime_put(mcasp->dev);
+ return 0;
+}
+
+static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+ int div)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1);
+}
+
+static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ pm_runtime_get_sync(mcasp->dev);
+ if (dir == SND_SOC_CLOCK_OUT) {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ } else {
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+ clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ }
+
+ mcasp->sysclk_freq = freq;
+
+ pm_runtime_put(mcasp->dev);
+ return 0;
+}
+
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
+ int serializers)
+{
+ struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
+ unsigned int *list = (unsigned int *) cl->list;
+ int slots = mcasp->tdm_slots;
+ int i, count = 0;
+
+ if (mcasp->tdm_mask[stream])
+ slots = hweight32(mcasp->tdm_mask[stream]);
+
+ for (i = 1; i <= slots; i++)
+ list[count++] = i;
+
+ for (i = 2; i <= serializers; i++)
+ list[count++] = i*slots;
+
+ cl->count = count;
+
+ return 0;
+}
+
+static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
+{
+ int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+ for (i = 0; i < mcasp->num_serializer; i++)
+ if (mcasp->serial_dir[i] == TX_MODE)
+ tx_serializers++;
+ else if (mcasp->serial_dir[i] == RX_MODE)
+ rx_serializers++;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
+ tx_serializers);
+ if (ret)
+ return ret;
+
+ ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
+ rx_serializers);
+
+ return ret;
+}
+
+
+static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(mcasp->dev,
+ "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
+ __func__, tx_mask, rx_mask, slots, slot_width);
+
+ if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(mcasp->dev,
+ "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+ tx_mask, rx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (slot_width &&
+ (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
+ dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ mcasp->tdm_slots = slots;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
+ mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+ mcasp->slot_width = slot_width;
+
+ return davinci_mcasp_set_ch_constraints(mcasp);
+}
+
+static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
+ int sample_width)
+{
+ u32 fmt;
+ u32 tx_rotate = (sample_width / 4) & 0x7;
+ u32 mask = (1ULL << sample_width) - 1;
+ u32 slot_width = sample_width;
+
+ /*
+ * For captured data we should not rotate, inversion and masking is
+ * enoguh to get the data to the right position:
+ * Format data from bus after reverse (XRBUF)
+ * S16_LE: |LSB|MSB|xxx|xxx| |xxx|xxx|MSB|LSB|
+ * S24_3LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB|
+ * S24_LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB|
+ * S32_LE: |LSB|DAT|DAT|MSB| |MSB|DAT|DAT|LSB|
+ */
+ u32 rx_rotate = 0;
+
+ /*
+ * Setting the tdm slot width either with set_clkdiv() or
+ * set_tdm_slot() allows us to for example send 32 bits per
+ * channel to the codec, while only 16 of them carry audio
+ * payload.
+ */
+ if (mcasp->slot_width) {
+ /*
+ * When we have more bclk then it is needed for the
+ * data, we need to use the rotation to move the
+ * received samples to have correct alignment.
+ */
+ slot_width = mcasp->slot_width;
+ rx_rotate = (slot_width - sample_width) / 4;
+ }
+
+ /* mapping of the XSSZ bit-field as described in the datasheet */
+ fmt = (slot_width >> 1) - 1;
+
+ if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+ RXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+ TXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+ TXROT(7));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+ RXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ }
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
+
+ return 0;
+}
+
+static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
+ int period_words, int channels)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream];
+ int i;
+ u8 tx_ser = 0;
+ u8 rx_ser = 0;
+ u8 slots = mcasp->tdm_slots;
+ u8 max_active_serializers = (channels + slots - 1) / slots;
+ int active_serializers, numevt;
+ u32 reg;
+ /* Default configuration */
+ if (mcasp->version < MCASP_VERSION_3)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+
+ /* All PINS as McASP */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+ } else {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS);
+ }
+
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ mcasp->serial_dir[i]);
+ if (mcasp->serial_dir[i] == TX_MODE &&
+ tx_ser < max_active_serializers) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ mcasp->dismod, DISMOD_MASK);
+ set_bit(PIN_BIT_AXR(i), &mcasp->pdir);
+ tx_ser++;
+ } else if (mcasp->serial_dir[i] == RX_MODE &&
+ rx_ser < max_active_serializers) {
+ clear_bit(PIN_BIT_AXR(i), &mcasp->pdir);
+ rx_ser++;
+ } else if (mcasp->serial_dir[i] == INACTIVE_MODE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ SRMOD_INACTIVE, SRMOD_MASK);
+ clear_bit(PIN_BIT_AXR(i), &mcasp->pdir);
+ } else if (mcasp->serial_dir[i] == TX_MODE) {
+ /* Unused TX pins, clear PDIR */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ mcasp->dismod, DISMOD_MASK);
+ clear_bit(PIN_BIT_AXR(i), &mcasp->pdir);
+ }
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ active_serializers = tx_ser;
+ numevt = mcasp->txnumevt;
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ } else {
+ active_serializers = rx_ser;
+ numevt = mcasp->rxnumevt;
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ }
+
+ if (active_serializers < max_active_serializers) {
+ dev_warn(mcasp->dev, "stream has more channels (%d) than are "
+ "enabled in mcasp (%d)\n", channels,
+ active_serializers * slots);
+ return -EINVAL;
+ }
+
+ /* AFIFO is not in use */
+ if (!numevt) {
+ /* Configure the burst size for platform drivers */
+ if (active_serializers > 1) {
+ /*
+ * If more than one serializers are in use we have one
+ * DMA request to provide data for all serializers.
+ * For example if three serializers are enabled the DMA
+ * need to transfer three words per DMA request.
+ */
+ dma_data->maxburst = active_serializers;
+ } else {
+ dma_data->maxburst = 0;
+ }
+ return 0;
+ }
+
+ if (period_words % active_serializers) {
+ dev_err(mcasp->dev, "Invalid combination of period words and "
+ "active serializers: %d, %d\n", period_words,
+ active_serializers);
+ return -EINVAL;
+ }
+
+ /*
+ * Calculate the optimal AFIFO depth for platform side:
+ * The number of words for numevt need to be in steps of active
+ * serializers.
+ */
+ numevt = (numevt / active_serializers) * active_serializers;
+
+ while (period_words % numevt && numevt > 0)
+ numevt -= active_serializers;
+ if (numevt <= 0)
+ numevt = active_serializers;
+
+ mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK);
+ mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK);
+
+ /* Configure the burst size for platform drivers */
+ if (numevt == 1)
+ numevt = 0;
+ dma_data->maxburst = numevt;
+
+ return 0;
+}
+
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
+ int channels)
+{
+ int i, active_slots;
+ int total_slots;
+ int active_serializers;
+ u32 mask = 0;
+ u32 busel = 0;
+
+ total_slots = mcasp->tdm_slots;
+
+ /*
+ * If more than one serializer is needed, then use them with
+ * all the specified tdm_slots. Otherwise, one serializer can
+ * cope with the transaction using just as many slots as there
+ * are channels in the stream.
+ */
+ if (mcasp->tdm_mask[stream]) {
+ active_slots = hweight32(mcasp->tdm_mask[stream]);
+ active_serializers = (channels + active_slots - 1) /
+ active_slots;
+ if (active_serializers == 1) {
+ active_slots = channels;
+ for (i = 0; i < total_slots; i++) {
+ if ((1 << i) & mcasp->tdm_mask[stream]) {
+ mask |= (1 << i);
+ if (--active_slots <= 0)
+ break;
+ }
+ }
+ }
+ } else {
+ active_serializers = (channels + total_slots - 1) / total_slots;
+ if (active_serializers == 1)
+ active_slots = channels;
+ else
+ active_slots = total_slots;
+
+ for (i = 0; i < active_slots; i++)
+ mask |= (1 << i);
+ }
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+
+ if (!mcasp->dat_port)
+ busel = TXSEL;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
+ } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
+ FSRMOD(total_slots), FSRMOD(0x1FF));
+ /*
+ * If McASP is set to be TX/RX synchronous and the playback is
+ * not running already we need to configure the TX slots in
+ * order to have correct FSX on the bus
+ */
+ if (mcasp_is_synchronous(mcasp) && !mcasp->channels)
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
+ FSXMOD(total_slots), FSXMOD(0x1FF));
+ }
+
+ return 0;
+}
+
+/* S/PDIF */
+static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
+ unsigned int rate)
+{
+ u32 cs_value = 0;
+ u8 *cs_bytes = (u8*) &cs_value;
+
+ /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
+ and LSB first */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15));
+
+ /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180));
+
+ /* Set the TX tdm : for all the slots */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+ /* Set the TX clock controls : div = 1 and internal */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC);
+
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+
+ /* Only 44100 and 48000 are valid, both have the same setting */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
+
+ /* Enable the DIT */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+
+ /* Set S/PDIF channel status bits */
+ cs_bytes[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
+ cs_bytes[1] = IEC958_AES1_CON_PCM_CODER;
+
+ switch (rate) {
+ case 22050:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ printk(KERN_WARNING "unsupported sampling rate: %d\n", rate);
+ return -EINVAL;
+ }
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRA_REG, cs_value);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRB_REG, cs_value);
+
+ return 0;
+}
+
+static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
+ unsigned int bclk_freq, bool set)
+{
+ int error_ppm;
+ unsigned int sysclk_freq = mcasp->sysclk_freq;
+ u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
+ int div = sysclk_freq / bclk_freq;
+ int rem = sysclk_freq % bclk_freq;
+ int aux_div = 1;
+
+ if (div > (ACLKXDIV_MASK + 1)) {
+ if (reg & AHCLKXE) {
+ aux_div = div / (ACLKXDIV_MASK + 1);
+ if (div % (ACLKXDIV_MASK + 1))
+ aux_div++;
+
+ sysclk_freq /= aux_div;
+ div = sysclk_freq / bclk_freq;
+ rem = sysclk_freq % bclk_freq;
+ } else if (set) {
+ dev_warn(mcasp->dev, "Too fast reference clock (%u)\n",
+ sysclk_freq);
+ }
+ }
+
+ if (rem != 0) {
+ if (div == 0 ||
+ ((sysclk_freq / div) - bclk_freq) >
+ (bclk_freq - (sysclk_freq / (div+1)))) {
+ div++;
+ rem = rem - bclk_freq;
+ }
+ }
+ error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem,
+ (int)bclk_freq)) / div - 1000000;
+
+ if (set) {
+ if (error_ppm)
+ dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
+ error_ppm);
+
+ __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
+ if (reg & AHCLKXE)
+ __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
+ aux_div, 0);
+ }
+
+ return error_ppm;
+}
+
+static inline u32 davinci_mcasp_tx_delay(struct davinci_mcasp *mcasp)
+{
+ if (!mcasp->txnumevt)
+ return 0;
+
+ return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_WFIFOSTS_OFFSET);
+}
+
+static inline u32 davinci_mcasp_rx_delay(struct davinci_mcasp *mcasp)
+{
+ if (!mcasp->rxnumevt)
+ return 0;
+
+ return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_RFIFOSTS_OFFSET);
+}
+
+static snd_pcm_sframes_t davinci_mcasp_delay(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 fifo_use;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ fifo_use = davinci_mcasp_tx_delay(mcasp);
+ else
+ fifo_use = davinci_mcasp_rx_delay(mcasp);
+
+ /*
+ * Divide the used locations with the channel count to get the
+ * FIFO usage in samples (don't care about partial samples in the
+ * buffer).
+ */
+ return fifo_use / substream->runtime->channels;
+}
+
+static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ int word_length;
+ int channels = params_channels(params);
+ int period_size = params_period_size(params);
+ int ret;
+
+ ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt);
+ if (ret)
+ return ret;
+
+ /*
+ * If mcasp is BCLK master, and a BCLK divider was not provided by
+ * the machine driver, we need to calculate the ratio.
+ */
+ if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
+ int slots = mcasp->tdm_slots;
+ int rate = params_rate(params);
+ int sbits = params_width(params);
+
+ if (mcasp->slot_width)
+ sbits = mcasp->slot_width;
+
+ davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true);
+ }
+
+ ret = mcasp_common_hw_param(mcasp, substream->stream,
+ period_size * channels, channels);
+ if (ret)
+ return ret;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ ret = mcasp_dit_hw_param(mcasp, params_rate(params));
+ else
+ ret = mcasp_i2s_hw_param(mcasp, substream->stream,
+ channels);
+
+ if (ret)
+ return ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ case SNDRV_PCM_FORMAT_S8:
+ word_length = 8;
+ break;
+
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ word_length = 16;
+ break;
+
+ case SNDRV_PCM_FORMAT_U24_3LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ word_length = 24;
+ break;
+
+ case SNDRV_PCM_FORMAT_U24_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ word_length = 24;
+ break;
+
+ case SNDRV_PCM_FORMAT_U32_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ word_length = 32;
+ break;
+
+ default:
+ printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ davinci_config_channel_size(mcasp, word_length);
+
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+ mcasp->channels = channels;
+
+ return 0;
+}
+
+static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_mcasp_start(mcasp, substream->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_mcasp_stop(mcasp, substream->stream);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const unsigned int davinci_mcasp_dai_rates[] = {
+ 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000,
+ 88200, 96000, 176400, 192000,
+};
+
+#define DAVINCI_MAX_RATE_ERROR_PPM 1000
+
+static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct davinci_mcasp_ruledata *rd = rule->private;
+ struct snd_interval *ri =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ int sbits = params_width(params);
+ int slots = rd->mcasp->tdm_slots;
+ struct snd_interval range;
+ int i;
+
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
+ snd_interval_any(&range);
+ range.empty = 1;
+
+ for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) {
+ if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) {
+ uint bclk_freq = sbits*slots*
+ davinci_mcasp_dai_rates[i];
+ int ppm;
+
+ ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq,
+ false);
+ if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
+ if (range.empty) {
+ range.min = davinci_mcasp_dai_rates[i];
+ range.empty = 0;
+ }
+ range.max = davinci_mcasp_dai_rates[i];
+ }
+ }
+ }
+
+ dev_dbg(rd->mcasp->dev,
+ "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n",
+ ri->min, ri->max, range.min, range.max, sbits, slots);
+
+ return snd_interval_refine(hw_param_interval(params, rule->var),
+ &range);
+}
+
+static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct davinci_mcasp_ruledata *rd = rule->private;
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ struct snd_mask nfmt;
+ int rate = params_rate(params);
+ int slots = rd->mcasp->tdm_slots;
+ int i, count = 0;
+
+ snd_mask_none(&nfmt);
+
+ for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+ if (snd_mask_test(fmt, i)) {
+ uint sbits = snd_pcm_format_width(i);
+ int ppm;
+
+ if (rd->mcasp->slot_width)
+ sbits = rd->mcasp->slot_width;
+
+ ppm = davinci_mcasp_calc_clk_div(rd->mcasp,
+ sbits * slots * rate,
+ false);
+ if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
+ snd_mask_set(&nfmt, i);
+ count++;
+ }
+ }
+ }
+ dev_dbg(rd->mcasp->dev,
+ "%d possible sample format for %d Hz and %d tdm slots\n",
+ count, rate, slots);
+
+ return snd_mask_refine(fmt, &nfmt);
+}
+
+static int davinci_mcasp_hw_rule_min_periodsize(
+ struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *period_size = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+ struct snd_interval frames;
+
+ snd_interval_any(&frames);
+ frames.min = 64;
+ frames.integer = 1;
+
+ return snd_interval_refine(period_size, &frames);
+}
+
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct davinci_mcasp_ruledata *ruledata =
+ &mcasp->ruledata[substream->stream];
+ u32 max_channels = 0;
+ int i, dir;
+ int tdm_slots = mcasp->tdm_slots;
+
+ /* Do not allow more then one stream per direction */
+ if (mcasp->substreams[substream->stream])
+ return -EBUSY;
+
+ mcasp->substreams[substream->stream] = substream;
+
+ if (mcasp->tdm_mask[substream->stream])
+ tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ return 0;
+
+ /*
+ * Limit the maximum allowed channels for the first stream:
+ * number of serializers for the direction * tdm slots per serializer
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = TX_MODE;
+ else
+ dir = RX_MODE;
+
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ if (mcasp->serial_dir[i] == dir)
+ max_channels++;
+ }
+ ruledata->serializers = max_channels;
+ max_channels *= tdm_slots;
+ /*
+ * If the already active stream has less channels than the calculated
+ * limnit based on the seirializers * tdm_slots, we need to use that as
+ * a constraint for the second stream.
+ * Otherwise (first stream or less allowed channels) we use the
+ * calculated constraint.
+ */
+ if (mcasp->channels && mcasp->channels < max_channels)
+ max_channels = mcasp->channels;
+ /*
+ * But we can always allow channels upto the amount of
+ * the available tdm_slots.
+ */
+ if (max_channels < tdm_slots)
+ max_channels = tdm_slots;
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 0, max_channels);
+
+ snd_pcm_hw_constraint_list(substream->runtime,
+ 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &mcasp->chconstr[substream->stream]);
+
+ if (mcasp->slot_width)
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ 8, mcasp->slot_width);
+
+ /*
+ * If we rely on implicit BCLK divider setting we should
+ * set constraints based on what we can provide.
+ */
+ if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
+ int ret;
+
+ ruledata->mcasp = mcasp;
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ davinci_mcasp_hw_rule_rate,
+ ruledata,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (ret)
+ return ret;
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ davinci_mcasp_hw_rule_format,
+ ruledata,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (ret)
+ return ret;
+ }
+
+ snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ davinci_mcasp_hw_rule_min_periodsize, NULL,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+
+ return 0;
+}
+
+static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+
+ mcasp->substreams[substream->stream] = NULL;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ return;
+
+ if (!cpu_dai->active)
+ mcasp->channels = 0;
+}
+
+static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+ .startup = davinci_mcasp_startup,
+ .shutdown = davinci_mcasp_shutdown,
+ .trigger = davinci_mcasp_trigger,
+ .delay = davinci_mcasp_delay,
+ .hw_params = davinci_mcasp_hw_params,
+ .set_fmt = davinci_mcasp_set_dai_fmt,
+ .set_clkdiv = davinci_mcasp_set_clkdiv,
+ .set_sysclk = davinci_mcasp_set_sysclk,
+ .set_tdm_slot = davinci_mcasp_set_tdm_slot,
+};
+
+static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int davinci_mcasp_suspend(struct snd_soc_dai *dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp_context *context = &mcasp->context;
+ u32 reg;
+ int i;
+
+ context->pm_state = pm_runtime_active(mcasp->dev);
+ if (!context->pm_state)
+ pm_runtime_get_sync(mcasp->dev);
+
+ for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+ context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]);
+
+ if (mcasp->txnumevt) {
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ context->afifo_regs[0] = mcasp_get_reg(mcasp, reg);
+ }
+ if (mcasp->rxnumevt) {
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ context->afifo_regs[1] = mcasp_get_reg(mcasp, reg);
+ }
+
+ for (i = 0; i < mcasp->num_serializer; i++)
+ context->xrsr_regs[i] = mcasp_get_reg(mcasp,
+ DAVINCI_MCASP_XRSRCTL_REG(i));
+
+ pm_runtime_put_sync(mcasp->dev);
+
+ return 0;
+}
+
+static int davinci_mcasp_resume(struct snd_soc_dai *dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+ struct davinci_mcasp_context *context = &mcasp->context;
+ u32 reg;
+ int i;
+
+ pm_runtime_get_sync(mcasp->dev);
+
+ for (i = 0; i < ARRAY_SIZE(context_regs); i++)
+ mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]);
+
+ if (mcasp->txnumevt) {
+ reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+ mcasp_set_reg(mcasp, reg, context->afifo_regs[0]);
+ }
+ if (mcasp->rxnumevt) {
+ reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+ mcasp_set_reg(mcasp, reg, context->afifo_regs[1]);
+ }
+
+ for (i = 0; i < mcasp->num_serializer; i++)
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i),
+ context->xrsr_regs[i]);
+
+ if (!context->pm_state)
+ pm_runtime_put_sync(mcasp->dev);
+
+ return 0;
+}
+#else
+#define davinci_mcasp_suspend NULL
+#define davinci_mcasp_resume NULL
+#endif
+
+#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
+
+#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_U8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_U24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_U24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
+ {
+ .name = "davinci-mcasp.0",
+ .probe = davinci_mcasp_dai_probe,
+ .suspend = davinci_mcasp_suspend,
+ .resume = davinci_mcasp_resume,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 32 * 16,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = DAVINCI_MCASP_PCM_FMTS,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 32 * 16,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = DAVINCI_MCASP_PCM_FMTS,
+ },
+ .ops = &davinci_mcasp_dai_ops,
+
+ .symmetric_samplebits = 1,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "davinci-mcasp.1",
+ .probe = davinci_mcasp_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 384,
+ .rates = DAVINCI_MCASP_RATES,
+ .formats = DAVINCI_MCASP_PCM_FMTS,
+ },
+ .ops = &davinci_mcasp_dai_ops,
+ },
+
+};
+
+static const struct snd_soc_component_driver davinci_mcasp_component = {
+ .name = "davinci-mcasp",
+};
+
+/* Some HW specific values and defaults. The rest is filled in from DT. */
+static struct davinci_mcasp_pdata dm646x_mcasp_pdata = {
+ .tx_dma_offset = 0x400,
+ .rx_dma_offset = 0x400,
+ .version = MCASP_VERSION_1,
+};
+
+static struct davinci_mcasp_pdata da830_mcasp_pdata = {
+ .tx_dma_offset = 0x2000,
+ .rx_dma_offset = 0x2000,
+ .version = MCASP_VERSION_2,
+};
+
+static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
+ .tx_dma_offset = 0,
+ .rx_dma_offset = 0,
+ .version = MCASP_VERSION_3,
+};
+
+static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
+ /* The CFG port offset will be calculated if it is needed */
+ .tx_dma_offset = 0,
+ .rx_dma_offset = 0,
+ .version = MCASP_VERSION_4,
+};
+
+static const struct of_device_id mcasp_dt_ids[] = {
+ {
+ .compatible = "ti,dm646x-mcasp-audio",
+ .data = &dm646x_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,da830-mcasp-audio",
+ .data = &da830_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,am33xx-mcasp-audio",
+ .data = &am33xx_mcasp_pdata,
+ },
+ {
+ .compatible = "ti,dra7-mcasp-audio",
+ .data = &dra7_mcasp_pdata,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mcasp_dt_ids);
+
+static int mcasp_reparent_fck(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct clk *gfclk, *parent_clk;
+ const char *parent_name;
+ int ret;
+
+ if (!node)
+ return 0;
+
+ parent_name = of_get_property(node, "fck_parent", NULL);
+ if (!parent_name)
+ return 0;
+
+ dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n");
+
+ gfclk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(gfclk)) {
+ dev_err(&pdev->dev, "failed to get fck\n");
+ return PTR_ERR(gfclk);
+ }
+
+ parent_clk = clk_get(NULL, parent_name);
+ if (IS_ERR(parent_clk)) {
+ dev_err(&pdev->dev, "failed to get parent clock\n");
+ ret = PTR_ERR(parent_clk);
+ goto err1;
+ }
+
+ ret = clk_set_parent(gfclk, parent_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to reparent fck\n");
+ goto err2;
+ }
+
+err2:
+ clk_put(parent_clk);
+err1:
+ clk_put(gfclk);
+ return ret;
+}
+
+static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
+ struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct davinci_mcasp_pdata *pdata = NULL;
+ const struct of_device_id *match =
+ of_match_device(mcasp_dt_ids, &pdev->dev);
+ struct of_phandle_args dma_spec;
+
+ const u32 *of_serial_dir32;
+ u32 val;
+ int i, ret = 0;
+
+ if (pdev->dev.platform_data) {
+ pdata = pdev->dev.platform_data;
+ pdata->dismod = DISMOD_LOW;
+ return pdata;
+ } else if (match) {
+ pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ return pdata;
+ }
+ } else {
+ /* control shouldn't reach here. something is wrong */
+ ret = -EINVAL;
+ goto nodata;
+ }
+
+ ret = of_property_read_u32(np, "op-mode", &val);
+ if (ret >= 0)
+ pdata->op_mode = val;
+
+ ret = of_property_read_u32(np, "tdm-slots", &val);
+ if (ret >= 0) {
+ if (val < 2 || val > 32) {
+ dev_err(&pdev->dev,
+ "tdm-slots must be in rage [2-32]\n");
+ ret = -EINVAL;
+ goto nodata;
+ }
+
+ pdata->tdm_slots = val;
+ }
+
+ of_serial_dir32 = of_get_property(np, "serial-dir", &val);
+ val /= sizeof(u32);
+ if (of_serial_dir32) {
+ u8 *of_serial_dir = devm_kzalloc(&pdev->dev,
+ (sizeof(*of_serial_dir) * val),
+ GFP_KERNEL);
+ if (!of_serial_dir) {
+ ret = -ENOMEM;
+ goto nodata;
+ }
+
+ for (i = 0; i < val; i++)
+ of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]);
+
+ pdata->num_serializer = val;
+ pdata->serial_dir = of_serial_dir;
+ }
+
+ ret = of_property_match_string(np, "dma-names", "tx");
+ if (ret < 0)
+ goto nodata;
+
+ ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret,
+ &dma_spec);
+ if (ret < 0)
+ goto nodata;
+
+ pdata->tx_dma_channel = dma_spec.args[0];
+
+ /* RX is not valid in DIT mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE) {
+ ret = of_property_match_string(np, "dma-names", "rx");
+ if (ret < 0)
+ goto nodata;
+
+ ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret,
+ &dma_spec);
+ if (ret < 0)
+ goto nodata;
+
+ pdata->rx_dma_channel = dma_spec.args[0];
+ }
+
+ ret = of_property_read_u32(np, "tx-num-evt", &val);
+ if (ret >= 0)
+ pdata->txnumevt = val;
+
+ ret = of_property_read_u32(np, "rx-num-evt", &val);
+ if (ret >= 0)
+ pdata->rxnumevt = val;
+
+ ret = of_property_read_u32(np, "sram-size-playback", &val);
+ if (ret >= 0)
+ pdata->sram_size_playback = val;
+
+ ret = of_property_read_u32(np, "sram-size-capture", &val);
+ if (ret >= 0)
+ pdata->sram_size_capture = val;
+
+ ret = of_property_read_u32(np, "dismod", &val);
+ if (ret >= 0) {
+ if (val == 0 || val == 2 || val == 3) {
+ pdata->dismod = DISMOD_VAL(val);
+ } else {
+ dev_warn(&pdev->dev, "Invalid dismod value: %u\n", val);
+ pdata->dismod = DISMOD_LOW;
+ }
+ } else {
+ pdata->dismod = DISMOD_LOW;
+ }
+
+ return pdata;
+
+nodata:
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error populating platform data, err %d\n",
+ ret);
+ pdata = NULL;
+ }
+ return pdata;
+}
+
+enum {
+ PCM_EDMA,
+ PCM_SDMA,
+};
+static const char *sdma_prefix = "ti,omap";
+
+static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
+{
+ struct dma_chan *chan;
+ const char *tmp;
+ int ret = PCM_EDMA;
+
+ if (!mcasp->dev->of_node)
+ return PCM_EDMA;
+
+ tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data;
+ chan = dma_request_slave_channel_reason(mcasp->dev, tmp);
+ if (IS_ERR(chan)) {
+ if (PTR_ERR(chan) != -EPROBE_DEFER)
+ dev_err(mcasp->dev,
+ "Can't verify DMA configuration (%ld)\n",
+ PTR_ERR(chan));
+ return PTR_ERR(chan);
+ }
+ if (WARN_ON(!chan->device || !chan->device->dev))
+ return -EINVAL;
+
+ if (chan->device->dev->of_node)
+ ret = of_property_read_string(chan->device->dev->of_node,
+ "compatible", &tmp);
+ else
+ dev_dbg(mcasp->dev, "DMA controller has no of-node\n");
+
+ dma_release_channel(chan);
+ if (ret)
+ return ret;
+
+ dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp);
+ if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix)))
+ return PCM_SDMA;
+
+ return PCM_EDMA;
+}
+
+static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->tx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == TX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_TXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
+static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->rx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == RX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_RXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
+static int davinci_mcasp_probe(struct platform_device *pdev)
+{
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ struct resource *mem, *res, *dat;
+ struct davinci_mcasp_pdata *pdata;
+ struct davinci_mcasp *mcasp;
+ char *irq_name;
+ int *dma;
+ int irq;
+ int ret;
+
+ if (!pdev->dev.platform_data && !pdev->dev.of_node) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp),
+ GFP_KERNEL);
+ if (!mcasp)
+ return -ENOMEM;
+
+ pdata = davinci_mcasp_set_pdata_from_of(pdev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ if (!mem) {
+ dev_warn(mcasp->dev,
+ "\"mpu\" mem resource not found, using index 0\n");
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ }
+
+ mcasp->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(mcasp->base))
+ return PTR_ERR(mcasp->base);
+
+ pm_runtime_enable(&pdev->dev);
+
+ mcasp->op_mode = pdata->op_mode;
+ /* sanity check for tdm slots parameter */
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+ if (pdata->tdm_slots < 2) {
+ dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+ pdata->tdm_slots);
+ mcasp->tdm_slots = 2;
+ } else if (pdata->tdm_slots > 32) {
+ dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+ pdata->tdm_slots);
+ mcasp->tdm_slots = 32;
+ } else {
+ mcasp->tdm_slots = pdata->tdm_slots;
+ }
+ }
+
+ mcasp->num_serializer = pdata->num_serializer;
+#ifdef CONFIG_PM_SLEEP
+ mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev,
+ mcasp->num_serializer, sizeof(u32),
+ GFP_KERNEL);
+ if (!mcasp->context.xrsr_regs) {
+ ret = -ENOMEM;
+ goto err;
+ }
+#endif
+ mcasp->serial_dir = pdata->serial_dir;
+ mcasp->version = pdata->version;
+ mcasp->txnumevt = pdata->txnumevt;
+ mcasp->rxnumevt = pdata->rxnumevt;
+ mcasp->dismod = pdata->dismod;
+
+ mcasp->dev = &pdev->dev;
+
+ irq = platform_get_irq_byname(pdev, "common");
+ if (irq >= 0) {
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common",
+ dev_name(&pdev->dev));
+ if (!irq_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_common_irq_handler,
+ IRQF_ONESHOT | IRQF_SHARED,
+ irq_name, mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "common IRQ request failed\n");
+ goto err;
+ }
+
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
+ }
+
+ irq = platform_get_irq_byname(pdev, "rx");
+ if (irq >= 0) {
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx",
+ dev_name(&pdev->dev));
+ if (!irq_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_rx_irq_handler,
+ IRQF_ONESHOT, irq_name, mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "RX IRQ request failed\n");
+ goto err;
+ }
+
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
+ }
+
+ irq = platform_get_irq_byname(pdev, "tx");
+ if (irq >= 0) {
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx",
+ dev_name(&pdev->dev));
+ if (!irq_name) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_tx_irq_handler,
+ IRQF_ONESHOT, irq_name, mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "TX IRQ request failed\n");
+ goto err;
+ }
+
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
+ }
+
+ dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
+ if (dat)
+ mcasp->dat_port = true;
+
+ dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ if (dat)
+ dma_data->addr = dat->start;
+ else
+ dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata);
+
+ dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (res)
+ *dma = res->start;
+ else
+ *dma = pdata->tx_dma_channel;
+
+ /* dmaengine filter data for DT and non-DT boot */
+ if (pdev->dev.of_node)
+ dma_data->filter_data = "tx";
+ else
+ dma_data->filter_data = dma;
+
+ /* RX is not valid in DIT mode */
+ if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
+ dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ if (dat)
+ dma_data->addr = dat->start;
+ else
+ dma_data->addr =
+ mem->start + davinci_mcasp_rxdma_offset(pdata);
+
+ dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (res)
+ *dma = res->start;
+ else
+ *dma = pdata->rx_dma_channel;
+
+ /* dmaengine filter data for DT and non-DT boot */
+ if (pdev->dev.of_node)
+ dma_data->filter_data = "rx";
+ else
+ dma_data->filter_data = dma;
+ }
+
+ if (mcasp->version < MCASP_VERSION_3) {
+ mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE;
+ /* dma_params->dma_addr is pointing to the data port address */
+ mcasp->dat_port = true;
+ } else {
+ mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
+ }
+
+ /* Allocate memory for long enough list for all possible
+ * scenarios. Maximum number tdm slots is 32 and there cannot
+ * be more serializers than given in the configuration. The
+ * serializer directions could be taken into account, but it
+ * would make code much more complex and save only couple of
+ * bytes.
+ */
+ mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
+ devm_kcalloc(mcasp->dev,
+ 32 + mcasp->num_serializer - 1,
+ sizeof(unsigned int),
+ GFP_KERNEL);
+
+ mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
+ devm_kcalloc(mcasp->dev,
+ 32 + mcasp->num_serializer - 1,
+ sizeof(unsigned int),
+ GFP_KERNEL);
+
+ if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
+ !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = davinci_mcasp_set_ch_constraints(mcasp);
+ if (ret)
+ goto err;
+
+ dev_set_drvdata(&pdev->dev, mcasp);
+
+ mcasp_reparent_fck(pdev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &davinci_mcasp_component,
+ &davinci_mcasp_dai[pdata->op_mode], 1);
+
+ if (ret != 0)
+ goto err;
+
+ ret = davinci_mcasp_get_dma_type(mcasp);
+ switch (ret) {
+ case PCM_EDMA:
+#if IS_BUILTIN(CONFIG_SND_SOC_TI_EDMA_PCM) || \
+ (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \
+ IS_MODULE(CONFIG_SND_SOC_TI_EDMA_PCM))
+ ret = edma_pcm_platform_register(&pdev->dev);
+#else
+ dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n");
+ ret = -EINVAL;
+ goto err;
+#endif
+ break;
+ case PCM_SDMA:
+#if IS_BUILTIN(CONFIG_SND_SOC_TI_SDMA_PCM) || \
+ (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \
+ IS_MODULE(CONFIG_SND_SOC_TI_SDMA_PCM))
+ ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
+#else
+ dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n");
+ ret = -EINVAL;
+ goto err;
+#endif
+ break;
+ default:
+ dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret);
+ case -EPROBE_DEFER:
+ goto err;
+ break;
+ }
+
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static int davinci_mcasp_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver davinci_mcasp_driver = {
+ .probe = davinci_mcasp_probe,
+ .remove = davinci_mcasp_remove,
+ .driver = {
+ .name = "davinci-mcasp",
+ .of_match_table = mcasp_dt_ids,
+ },
+};
+
+module_platform_driver(davinci_mcasp_driver);
+
+MODULE_AUTHOR("Steve Chen");
+MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ * Suresh Rajashekara <suresh.r@ti.com>
+ * Steve Chen <schen@.mvista.com>
+ *
+ * Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright: (C) 2009 Texas Instruments, India
+ *
+ * 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 DAVINCI_MCASP_H
+#define DAVINCI_MCASP_H
+
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG 0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG 0x04
+
+#define DAVINCI_MCASP_PFUNC_REG 0x10
+#define DAVINCI_MCASP_PDIR_REG 0x14
+#define DAVINCI_MCASP_PDOUT_REG 0x18
+#define DAVINCI_MCASP_PDSET_REG 0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG 0x20
+
+#define DAVINCI_MCASP_TLGC_REG 0x30
+#define DAVINCI_MCASP_TLMR_REG 0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG 0x44
+#define DAVINCI_MCASP_AMUTE_REG 0x48
+#define DAVINCI_MCASP_LBCTL_REG 0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG 0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG 0x60
+#define DAVINCI_MCASP_RXMASK_REG 0x64
+#define DAVINCI_MCASP_RXFMT_REG 0x68
+#define DAVINCI_MCASP_RXFMCTL_REG 0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG 0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74
+#define DAVINCI_MCASP_RXTDM_REG 0x78
+#define DAVINCI_MCASP_EVTCTLR_REG 0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG 0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG 0x88
+#define DAVINCI_MCASP_REVTCTL_REG 0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG 0xa0
+#define DAVINCI_MCASP_TXMASK_REG 0xa4
+#define DAVINCI_MCASP_TXFMT_REG 0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG 0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4
+#define DAVINCI_MCASP_TXTDM_REG 0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG 0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG 0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG 0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG 0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG 0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG 0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG 0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+ (n << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2))
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2))
+
+/* McASP FIFO Registers */
+#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
+#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000)
+
+/* FIFO register offsets from AFIFO base */
+#define MCASP_WFIFOCTL_OFFSET (0x0)
+#define MCASP_WFIFOSTS_OFFSET (0x4)
+#define MCASP_RFIFOCTL_OFFSET (0x8)
+#define MCASP_RFIFOSTS_OFFSET (0xc)
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ * Register Bits
+ */
+#define MCASP_FREE BIT(0)
+#define MCASP_SOFT BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ * DAVINCI_MCASP_PDOUT_REG - Pin output in GPIO mode
+ * DAVINCI_MCASP_PDSET_REG - Pin input in GPIO mode
+ */
+#define PIN_BIT_AXR(n) (n)
+#define PIN_BIT_AMUTE 25
+#define PIN_BIT_ACLKX 26
+#define PIN_BIT_AHCLKX 27
+#define PIN_BIT_AFSX 28
+#define PIN_BIT_ACLKR 29
+#define PIN_BIT_AHCLKR 30
+#define PIN_BIT_AFSR 31
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN BIT(0) /* Transmit DIT mode enable/disable */
+#define VA BIT(2)
+#define VB BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val) (val)
+#define TXSEL BIT(3)
+#define TXSSZ(val) (val<<4)
+#define TXPBIT(val) (val<<8)
+#define TXPAD(val) (val<<13)
+#define TXORD BIT(15)
+#define FSXDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val) (val)
+#define RXSEL BIT(3)
+#define RXSSZ(val) (val<<4)
+#define RXPBIT(val) (val<<8)
+#define RXPAD(val) (val<<13)
+#define RXORD BIT(15)
+#define FSRDLY(val) (val<<16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits
+ */
+#define FSXPOL BIT(0)
+#define AFSXE BIT(1)
+#define FSXDUR BIT(4)
+#define FSXMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL BIT(0)
+#define AFSRE BIT(1)
+#define FSRDUR BIT(4)
+#define FSRMOD(val) (val<<7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val) (val)
+#define ACLKXE BIT(5)
+#define TX_ASYNC BIT(6)
+#define ACLKXPOL BIT(7)
+#define ACLKXDIV_MASK 0x1f
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val) (val)
+#define ACLKRE BIT(5)
+#define RX_ASYNC BIT(6)
+#define ACLKRPOL BIT(7)
+#define ACLKRDIV_MASK 0x1f
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ * Register Bits
+ */
+#define AHCLKXDIV(val) (val)
+#define AHCLKXPOL BIT(14)
+#define AHCLKXE BIT(15)
+#define AHCLKXDIV_MASK 0xfff
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ * Register Bits
+ */
+#define AHCLKRDIV(val) (val)
+#define AHCLKRPOL BIT(14)
+#define AHCLKRE BIT(15)
+#define AHCLKRDIV_MASK 0xfff
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits
+ */
+#define MODE(val) (val)
+#define DISMOD_3STATE (0x0)
+#define DISMOD_LOW (0x2 << 2)
+#define DISMOD_HIGH (0x3 << 2)
+#define DISMOD_VAL(x) ((x) << 2)
+#define DISMOD_MASK DISMOD_HIGH
+#define TXSTATE BIT(4)
+#define RXSTATE BIT(5)
+#define SRMOD_MASK 3
+#define SRMOD_INACTIVE 0
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN BIT(0)
+#define LBORD BIT(1)
+#define LBGENMODE(val) (val<<2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n) (1<<n)
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG - Global Control Register Bits
+ */
+#define RXCLKRST BIT(0) /* Receiver Clock Divider Reset */
+#define RXHCLKRST BIT(1) /* Receiver High Frequency Clock Divider */
+#define RXSERCLR BIT(2) /* Receiver Serializer Clear */
+#define RXSMRST BIT(3) /* Receiver State Machine Reset */
+#define RXFSRST BIT(4) /* Frame Sync Generator Reset */
+#define TXCLKRST BIT(8) /* Transmitter Clock Divider Reset */
+#define TXHCLKRST BIT(9) /* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR BIT(10) /* Transmit Serializer Clear */
+#define TXSMRST BIT(11) /* Transmitter State Machine Reset */
+#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRERR BIT(8) /* Transmit/Receive error */
+#define XRDATA BIT(5) /* Transmit/Receive data ready */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
+ */
+#define MUTENA(val) (val)
+#define MUTEINPOL BIT(2)
+#define MUTEINENA BIT(3)
+#define MUTEIN BIT(4)
+#define MUTER BIT(5)
+#define MUTEX BIT(6)
+#define MUTEFSR BIT(7)
+#define MUTEFSX BIT(8)
+#define MUTEBADCLKR BIT(9)
+#define MUTEBADCLKX BIT(10)
+#define MUTERXDMAERR BIT(11)
+#define MUTETXDMAERR BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN BIT(0)
+
+/*
+ * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
+ */
+#define FIFO_ENABLE BIT(16)
+#define NUMEVT_MASK (0xFF << 8)
+#define NUMEVT(x) (((x) & 0xFF) << 8)
+#define NUMDMA_MASK (0xFF)
+
+/* clock divider IDs */
+#define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */
+#define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */
+#define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */
+
+#endif /* DAVINCI_MCASP_H */
--- /dev/null
+/*
+ * ALSA SoC Voice Codec Interface for TI DAVINCI processor
+ *
+ * Copyright (C) 2010 Texas Instruments.
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/mfd/davinci_voicecodec.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "edma-pcm.h"
+#include "davinci-i2s.h"
+
+#define MOD_REG_BIT(val, mask, set) do { \
+ if (set) { \
+ val |= mask; \
+ } else { \
+ val &= ~mask; \
+ } \
+} while (0)
+
+struct davinci_vcif_dev {
+ struct davinci_vc *davinci_vc;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ int dma_request[2];
+};
+
+static void davinci_vcif_start(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_vcif_dev *davinci_vcif_dev =
+ snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
+ u32 w;
+
+ /* Start the sample generator and enable transmitter/receiver */
+ w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0);
+ else
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0);
+
+ writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
+}
+
+static void davinci_vcif_stop(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct davinci_vcif_dev *davinci_vcif_dev =
+ snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
+ u32 w;
+
+ /* Reset transmitter/receiver and sample rate/frame sync generators */
+ w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1);
+ else
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1);
+
+ writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
+}
+
+static int davinci_vcif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai);
+ struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc;
+ u32 w;
+
+ /* Restart the codec before setup */
+ davinci_vcif_stop(substream);
+ davinci_vcif_start(substream);
+
+ /* General line settings */
+ writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL);
+
+ writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR);
+
+ writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN);
+
+ w = readl(davinci_vc->base + DAVINCI_VC_CTRL);
+
+ /* Determine xfer data type */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U8:
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
+ DAVINCI_VC_CTRL_RD_UNSIGNED |
+ DAVINCI_VC_CTRL_WD_BITS_8 |
+ DAVINCI_VC_CTRL_WD_UNSIGNED, 1);
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
+ DAVINCI_VC_CTRL_WD_BITS_8, 1);
+
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED |
+ DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 |
+ DAVINCI_VC_CTRL_RD_UNSIGNED |
+ DAVINCI_VC_CTRL_WD_BITS_8 |
+ DAVINCI_VC_CTRL_WD_UNSIGNED, 0);
+ break;
+ default:
+ printk(KERN_WARNING "davinci-vcif: unsupported PCM format");
+ return -EINVAL;
+ }
+
+ writel(w, davinci_vc->base + DAVINCI_VC_CTRL);
+
+ return 0;
+}
+
+static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ davinci_vcif_start(substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ davinci_vcif_stop(substream);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000
+
+static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
+ .trigger = davinci_vcif_trigger,
+ .hw_params = davinci_vcif_hw_params,
+};
+
+static int davinci_vcif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver davinci_vcif_dai = {
+ .probe = davinci_vcif_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DAVINCI_VCIF_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = DAVINCI_VCIF_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .ops = &davinci_vcif_dai_ops,
+
+};
+
+static const struct snd_soc_component_driver davinci_vcif_component = {
+ .name = "davinci-vcif",
+};
+
+static int davinci_vcif_probe(struct platform_device *pdev)
+{
+ struct davinci_vc *davinci_vc = pdev->dev.platform_data;
+ struct davinci_vcif_dev *davinci_vcif_dev;
+ int ret;
+
+ davinci_vcif_dev = devm_kzalloc(&pdev->dev,
+ sizeof(struct davinci_vcif_dev),
+ GFP_KERNEL);
+ if (!davinci_vcif_dev)
+ return -ENOMEM;
+
+ /* DMA tx params */
+ davinci_vcif_dev->davinci_vc = davinci_vc;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data =
+ &davinci_vc->davinci_vcif.dma_tx_channel;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
+ davinci_vc->davinci_vcif.dma_tx_addr;
+
+ /* DMA rx params */
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data =
+ &davinci_vc->davinci_vcif.dma_rx_channel;
+ davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
+ davinci_vc->davinci_vcif.dma_rx_addr;
+
+ dev_set_drvdata(&pdev->dev, davinci_vcif_dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &davinci_vcif_component,
+ &davinci_vcif_dai, 1);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "could not register dai\n");
+ return ret;
+ }
+
+ ret = edma_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver davinci_vcif_driver = {
+ .probe = davinci_vcif_probe,
+ .driver = {
+ .name = "davinci-vcif",
+ },
+};
+
+module_platform_driver(davinci_vcif_driver);
+
+MODULE_AUTHOR("Miguel Aguilar");
+MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * edma-pcm.c - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Based on: sound/soc/tegra/tegra_pcm.c
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/edma.h>
+
+#include "edma-pcm.h"
+
+static const struct snd_pcm_hardware edma_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .buffer_bytes_max = 128 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 64 * 1024,
+ .periods_min = 2,
+ .periods_max = 19, /* Limit by edma dmaengine driver */
+};
+
+static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = {
+ .pcm_hardware = &edma_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .compat_filter_fn = edma_filter_fn,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+int edma_pcm_platform_register(struct device *dev)
+{
+ return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
+}
+EXPORT_SYMBOL_GPL(edma_pcm_platform_register);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("eDMA PCM ASoC platform driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx
+ *
+ * Copyright (C) 2014 Texas Instruments, Inc.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * Based on: sound/soc/tegra/tegra_pcm.h
+ *
+ * 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.
+ */
+
+#ifndef __EDMA_PCM_H__
+#define __EDMA_PCM_H__
+
+#if IS_ENABLED(CONFIG_SND_SOC_TI_EDMA_PCM)
+int edma_pcm_platform_register(struct device *dev);
+#else
+static inline int edma_pcm_platform_register(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_SND_SOC_TI_EDMA_PCM */
+
+#endif /* __EDMA_PCM_H__ */
--- /dev/null
+/*
+ * n810.c -- SoC audio for Nokia N810
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#include "omap-mcbsp.h"
+
+#define N810_HEADSET_AMP_GPIO 10
+#define N810_SPEAKER_AMP_GPIO 101
+
+enum {
+ N810_JACK_DISABLED,
+ N810_JACK_HP,
+ N810_JACK_HS,
+ N810_JACK_MIC,
+};
+
+static struct clk *sys_clkout2;
+static struct clk *sys_clkout2_src;
+static struct clk *func96m_clk;
+
+static int n810_spk_func;
+static int n810_jack_func;
+static int n810_dmic_func;
+
+static void n810_ext_control(struct snd_soc_dapm_context *dapm)
+{
+ int hp = 0, line1l = 0;
+
+ switch (n810_jack_func) {
+ case N810_JACK_HS:
+ line1l = 1;
+ case N810_JACK_HP:
+ hp = 1;
+ break;
+ case N810_JACK_MIC:
+ line1l = 1;
+ break;
+ }
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (n810_spk_func)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
+
+ if (hp)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ if (line1l)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
+
+ if (n810_dmic_func)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int n810_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+
+ n810_ext_control(&rtd->card->dapm);
+ return clk_prepare_enable(sys_clkout2);
+}
+
+static void n810_shutdown(struct snd_pcm_substream *substream)
+{
+ clk_disable_unprepare(sys_clkout2);
+}
+
+static int n810_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int err;
+
+ /* Set the codec system clock for DAC and ADC */
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000,
+ SND_SOC_CLOCK_IN);
+
+ return err;
+}
+
+static const struct snd_soc_ops n810_ops = {
+ .startup = n810_startup,
+ .hw_params = n810_hw_params,
+ .shutdown = n810_shutdown,
+};
+
+static int n810_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = n810_spk_func;
+
+ return 0;
+}
+
+static int n810_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (n810_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ n810_spk_func = ucontrol->value.enumerated.item[0];
+ n810_ext_control(&card->dapm);
+
+ return 1;
+}
+
+static int n810_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = n810_jack_func;
+
+ return 0;
+}
+
+static int n810_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (n810_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ n810_jack_func = ucontrol->value.enumerated.item[0];
+ n810_ext_control(&card->dapm);
+
+ return 1;
+}
+
+static int n810_get_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = n810_dmic_func;
+
+ return 0;
+}
+
+static int n810_set_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (n810_dmic_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ n810_dmic_func = ucontrol->value.enumerated.item[0];
+ n810_ext_control(&card->dapm);
+
+ return 1;
+}
+
+static int n810_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpio_set_value(N810_SPEAKER_AMP_GPIO, 1);
+ else
+ gpio_set_value(N810_SPEAKER_AMP_GPIO, 0);
+
+ return 0;
+}
+
+static int n810_jack_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpio_set_value(N810_HEADSET_AMP_GPIO, 1);
+ else
+ gpio_set_value(N810_HEADSET_AMP_GPIO, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event),
+ SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event),
+ SND_SOC_DAPM_MIC("DMic", NULL),
+ SND_SOC_DAPM_MIC("HS Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "HPLOUT"},
+ {"Headphone Jack", NULL, "HPROUT"},
+
+ {"Ext Spk", NULL, "LLOUT"},
+ {"Ext Spk", NULL, "RLOUT"},
+
+ {"DMic Rate 64", NULL, "DMic"},
+ {"DMic", NULL, "Mic Bias"},
+
+ /*
+ * Note that the mic bias is coming from Retu/Vilma and we don't have
+ * control over it atm. The analog HS mic is not working. <- TODO
+ */
+ {"LINE1L", NULL, "HS Mic"},
+};
+
+static const char *spk_function[] = {"Off", "On"};
+static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
+static const char *input_function[] = {"ADC", "Digital Mic"};
+static const struct soc_enum n810_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
+};
+
+static const struct snd_kcontrol_new aic33_n810_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", n810_enum[0],
+ n810_get_spk, n810_set_spk),
+ SOC_ENUM_EXT("Jack Function", n810_enum[1],
+ n810_get_jack, n810_set_jack),
+ SOC_ENUM_EXT("Input Select", n810_enum[2],
+ n810_get_input, n810_set_input),
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link n810_dai = {
+ .name = "TLV320AIC33",
+ .stream_name = "AIC33",
+ .cpu_dai_name = "48076000.mcbsp",
+ .platform_name = "48076000.mcbsp",
+ .codec_name = "tlv320aic3x-codec.1-0018",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .ops = &n810_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_n810 = {
+ .name = "N810",
+ .owner = THIS_MODULE,
+ .dai_link = &n810_dai,
+ .num_links = 1,
+
+ .controls = aic33_n810_controls,
+ .num_controls = ARRAY_SIZE(aic33_n810_controls),
+ .dapm_widgets = aic33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .fully_routed = true,
+};
+
+static struct platform_device *n810_snd_device;
+
+static int __init n810_soc_init(void)
+{
+ int err;
+ struct device *dev;
+
+ if (!of_have_populated_dt() ||
+ (!of_machine_is_compatible("nokia,n810") &&
+ !of_machine_is_compatible("nokia,n810-wimax")))
+ return -ENODEV;
+
+ n810_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!n810_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(n810_snd_device, &snd_soc_n810);
+ err = platform_device_add(n810_snd_device);
+ if (err)
+ goto err1;
+
+ dev = &n810_snd_device->dev;
+
+ sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
+ if (IS_ERR(sys_clkout2_src)) {
+ dev_err(dev, "Could not get sys_clkout2_src clock\n");
+ err = PTR_ERR(sys_clkout2_src);
+ goto err2;
+ }
+ sys_clkout2 = clk_get(dev, "sys_clkout2");
+ if (IS_ERR(sys_clkout2)) {
+ dev_err(dev, "Could not get sys_clkout2\n");
+ err = PTR_ERR(sys_clkout2);
+ goto err3;
+ }
+ /*
+ * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
+ * 96 MHz as its parent in order to get 12 MHz
+ */
+ func96m_clk = clk_get(dev, "func_96m_ck");
+ if (IS_ERR(func96m_clk)) {
+ dev_err(dev, "Could not get func 96M clock\n");
+ err = PTR_ERR(func96m_clk);
+ goto err4;
+ }
+ clk_set_parent(sys_clkout2_src, func96m_clk);
+ clk_set_rate(sys_clkout2, 12000000);
+
+ if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
+ (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) {
+ err = -EINVAL;
+ goto err4;
+ }
+
+ gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
+ gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
+
+ return 0;
+err4:
+ clk_put(sys_clkout2);
+err3:
+ clk_put(sys_clkout2_src);
+err2:
+ platform_device_del(n810_snd_device);
+err1:
+ platform_device_put(n810_snd_device);
+
+ return err;
+}
+
+static void __exit n810_soc_exit(void)
+{
+ gpio_free(N810_SPEAKER_AMP_GPIO);
+ gpio_free(N810_HEADSET_AMP_GPIO);
+ clk_put(sys_clkout2_src);
+ clk_put(sys_clkout2);
+ clk_put(func96m_clk);
+
+ platform_device_unregister(n810_snd_device);
+}
+
+module_init(n810_soc_init);
+module_exit(n810_soc_exit);
+
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
+MODULE_DESCRIPTION("ALSA SoC Nokia N810");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * omap-abe-twl6040.c -- SoC audio for TI OMAP based boards with ABE and
+ * twl6040 codec
+ *
+ * Author: Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "omap-dmic.h"
+#include "omap-mcpdm.h"
+#include "../codecs/twl6040.h"
+
+struct abe_twl6040 {
+ struct snd_soc_card card;
+ struct snd_soc_dai_link dai_links[2];
+ int jack_detection; /* board can detect jack events */
+ int mclk_freq; /* MCLK frequency speed for twl6040 */
+};
+
+static struct platform_device *dmic_codec_dev;
+
+static int omap_abe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
+ int clk_id, freq;
+ int ret;
+
+ clk_id = twl6040_get_clk_id(codec_dai->component);
+ if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
+ freq = priv->mclk_freq;
+ else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
+ freq = 32768;
+ else
+ return -EINVAL;
+
+ /* set the codec mclk */
+ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq,
+ SND_SOC_CLOCK_IN);
+ if (ret) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return ret;
+ }
+ return ret;
+}
+
+static const struct snd_soc_ops omap_abe_ops = {
+ .hw_params = omap_abe_hw_params,
+};
+
+static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret = 0;
+
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS,
+ 19200000, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set DMIC cpu system clock\n");
+ return ret;
+ }
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000,
+ SND_SOC_CLOCK_OUT);
+ if (ret < 0) {
+ printk(KERN_ERR "can't set DMIC output clock\n");
+ return ret;
+ }
+ return 0;
+}
+
+static struct snd_soc_ops omap_abe_dmic_ops = {
+ .hw_params = omap_abe_dmic_hw_params,
+};
+
+/* Headset jack */
+static struct snd_soc_jack hs_jack;
+
+/*Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headset Stereophone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* SDP4430 machine DAPM */
+static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
+ /* Outputs */
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+ SND_SOC_DAPM_SPK("Earphone Spk", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_SPK("Vibrator", NULL),
+
+ /* Inputs */
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+
+ /* Digital microphones */
+ SND_SOC_DAPM_MIC("Digital Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Routings for outputs */
+ {"Headset Stereophone", NULL, "HSOL"},
+ {"Headset Stereophone", NULL, "HSOR"},
+
+ {"Earphone Spk", NULL, "EP"},
+
+ {"Ext Spk", NULL, "HFL"},
+ {"Ext Spk", NULL, "HFR"},
+
+ {"Line Out", NULL, "AUXL"},
+ {"Line Out", NULL, "AUXR"},
+
+ {"Vibrator", NULL, "VIBRAL"},
+ {"Vibrator", NULL, "VIBRAR"},
+
+ /* Routings for inputs */
+ {"HSMIC", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "Headset Mic Bias"},
+
+ {"MAINMIC", NULL, "Main Handset Mic"},
+ {"Main Handset Mic", NULL, "Main Mic Bias"},
+
+ {"SUBMIC", NULL, "Sub Handset Mic"},
+ {"Sub Handset Mic", NULL, "Main Mic Bias"},
+
+ {"AFML", NULL, "Line In"},
+ {"AFMR", NULL, "Line In"},
+};
+
+static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *component = rtd->codec_dai->component;
+ struct snd_soc_card *card = rtd->card;
+ struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
+ int hs_trim;
+ int ret = 0;
+
+ /*
+ * Configure McPDM offset cancellation based on the HSOTRIM value from
+ * twl6040.
+ */
+ hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM);
+ omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim),
+ TWL6040_HSF_TRIM_RIGHT(hs_trim));
+
+ /* Headset jack detection only if it is supported */
+ if (priv->jack_detection) {
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET, &hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
+ if (ret)
+ return ret;
+
+ twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_route dmic_audio_map[] = {
+ {"DMic", NULL, "Digital Mic"},
+ {"Digital Mic", NULL, "Digital Mic1 Bias"},
+};
+
+static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+ return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
+ ARRAY_SIZE(dmic_audio_map));
+}
+
+static int omap_abe_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct snd_soc_card *card;
+ struct device_node *dai_node;
+ struct abe_twl6040 *priv;
+ int num_links = 0;
+ int ret = 0;
+
+ if (!node) {
+ dev_err(&pdev->dev, "of node is missing.\n");
+ return -ENODEV;
+ }
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ card = &priv->card;
+ card->dev = &pdev->dev;
+ card->owner = THIS_MODULE;
+ card->dapm_widgets = twl6040_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets);
+ card->dapm_routes = audio_map;
+ card->num_dapm_routes = ARRAY_SIZE(audio_map);
+
+ if (snd_soc_of_parse_card_name(card, "ti,model")) {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "Error while parsing DAPM routing\n");
+ return ret;
+ }
+
+ dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McPDM node is not provided\n");
+ return -EINVAL;
+ }
+
+ priv->dai_links[0].name = "DMIC";
+ priv->dai_links[0].stream_name = "TWL6040";
+ priv->dai_links[0].cpu_of_node = dai_node;
+ priv->dai_links[0].platform_of_node = dai_node;
+ priv->dai_links[0].codec_dai_name = "twl6040-legacy";
+ priv->dai_links[0].codec_name = "twl6040-codec";
+ priv->dai_links[0].init = omap_abe_twl6040_init;
+ priv->dai_links[0].ops = &omap_abe_ops;
+
+ dai_node = of_parse_phandle(node, "ti,dmic", 0);
+ if (dai_node) {
+ num_links = 2;
+ priv->dai_links[1].name = "TWL6040";
+ priv->dai_links[1].stream_name = "DMIC Capture";
+ priv->dai_links[1].cpu_of_node = dai_node;
+ priv->dai_links[1].platform_of_node = dai_node;
+ priv->dai_links[1].codec_dai_name = "dmic-hifi";
+ priv->dai_links[1].codec_name = "dmic-codec";
+ priv->dai_links[1].init = omap_abe_dmic_init;
+ priv->dai_links[1].ops = &omap_abe_dmic_ops;
+ } else {
+ num_links = 1;
+ }
+
+ priv->jack_detection = of_property_read_bool(node, "ti,jack-detection");
+ of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq);
+ if (!priv->mclk_freq) {
+ dev_err(&pdev->dev, "MCLK frequency not provided\n");
+ return -EINVAL;
+ }
+
+ card->fully_routed = 1;
+
+ if (!priv->mclk_freq) {
+ dev_err(&pdev->dev, "MCLK frequency missing\n");
+ return -ENODEV;
+ }
+
+ card->dai_link = priv->dai_links;
+ card->num_links = num_links;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
+ ret);
+
+ return ret;
+}
+
+static const struct of_device_id omap_abe_of_match[] = {
+ {.compatible = "ti,abe-twl6040", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_abe_of_match);
+
+static struct platform_driver omap_abe_driver = {
+ .driver = {
+ .name = "omap-abe-twl6040",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = omap_abe_of_match,
+ },
+ .probe = omap_abe_probe,
+};
+
+static int __init omap_abe_init(void)
+{
+ int ret;
+
+ dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL,
+ 0);
+ if (IS_ERR(dmic_codec_dev)) {
+ pr_err("%s: dmic-codec device registration failed\n", __func__);
+ return PTR_ERR(dmic_codec_dev);
+ }
+
+ ret = platform_driver_register(&omap_abe_driver);
+ if (ret) {
+ pr_err("%s: platform driver registration failed\n", __func__);
+ platform_device_unregister(dmic_codec_dev);
+ }
+
+ return ret;
+}
+module_init(omap_abe_init);
+
+static void __exit omap_abe_exit(void)
+{
+ platform_driver_unregister(&omap_abe_driver);
+ platform_device_unregister(dmic_codec_dev);
+}
+module_exit(omap_abe_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-abe-twl6040");
--- /dev/null
+/*
+ * omap-dmic.c -- OMAP ASoC DMIC DAI driver
+ *
+ * Copyright (C) 2010 - 2011 Texas Instruments
+ *
+ * Author: David Lambert <dlambert@ti.com>
+ * Misael Lopez Cruz <misael.lopez@ti.com>
+ * Liam Girdwood <lrg@ti.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "omap-dmic.h"
+#include "sdma-pcm.h"
+
+struct omap_dmic {
+ struct device *dev;
+ void __iomem *io_base;
+ struct clk *fclk;
+ struct pm_qos_request pm_qos_req;
+ int latency;
+ int fclk_freq;
+ int out_freq;
+ int clk_div;
+ int sysclk;
+ int threshold;
+ u32 ch_enabled;
+ bool active;
+ struct mutex mutex;
+
+ struct snd_dmaengine_dai_dma_data dma_data;
+};
+
+static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val)
+{
+ writel_relaxed(val, dmic->io_base + reg);
+}
+
+static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg)
+{
+ return readl_relaxed(dmic->io_base + reg);
+}
+
+static inline void omap_dmic_start(struct omap_dmic *dmic)
+{
+ u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
+
+ /* Configure DMA controller */
+ omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG,
+ OMAP_DMIC_DMA_ENABLE);
+
+ omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled);
+}
+
+static inline void omap_dmic_stop(struct omap_dmic *dmic)
+{
+ u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
+ omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
+ ctrl & ~OMAP_DMIC_UP_ENABLE_MASK);
+
+ /* Disable DMA request generation */
+ omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG,
+ OMAP_DMIC_DMA_ENABLE);
+
+}
+
+static inline int dmic_is_enabled(struct omap_dmic *dmic)
+{
+ return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) &
+ OMAP_DMIC_UP_ENABLE_MASK;
+}
+
+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);
+ int ret = 0;
+
+ mutex_lock(&dmic->mutex);
+
+ if (!dai->active)
+ dmic->active = 1;
+ else
+ ret = -EBUSY;
+
+ mutex_unlock(&dmic->mutex);
+
+ return ret;
+}
+
+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);
+
+ mutex_lock(&dmic->mutex);
+
+ pm_qos_remove_request(&dmic->pm_qos_req);
+
+ if (!dai->active)
+ dmic->active = 0;
+
+ mutex_unlock(&dmic->mutex);
+}
+
+static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate)
+{
+ int divider = -EINVAL;
+
+ /*
+ * 192KHz rate is only supported with 19.2MHz/3.84MHz clock
+ * configuration.
+ */
+ if (sample_rate == 192000) {
+ if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000)
+ divider = 0x6; /* Divider: 5 (192KHz sampling rate) */
+ else
+ dev_err(dmic->dev,
+ "invalid clock configuration for 192KHz\n");
+
+ return divider;
+ }
+
+ switch (dmic->out_freq) {
+ case 1536000:
+ if (dmic->fclk_freq != 24576000)
+ goto div_err;
+ divider = 0x4; /* Divider: 16 */
+ break;
+ case 2400000:
+ switch (dmic->fclk_freq) {
+ case 12000000:
+ divider = 0x5; /* Divider: 5 */
+ break;
+ case 19200000:
+ divider = 0x0; /* Divider: 8 */
+ break;
+ case 24000000:
+ divider = 0x2; /* Divider: 10 */
+ break;
+ default:
+ goto div_err;
+ }
+ break;
+ case 3072000:
+ if (dmic->fclk_freq != 24576000)
+ goto div_err;
+ divider = 0x3; /* Divider: 8 */
+ break;
+ case 3840000:
+ if (dmic->fclk_freq != 19200000)
+ goto div_err;
+ divider = 0x1; /* Divider: 5 (96KHz sampling rate) */
+ break;
+ default:
+ dev_err(dmic->dev, "invalid out frequency: %dHz\n",
+ dmic->out_freq);
+ break;
+ }
+
+ return divider;
+
+div_err:
+ dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n",
+ dmic->out_freq, dmic->fclk_freq);
+ return -EINVAL;
+}
+
+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 snd_dmaengine_dai_dma_data *dma_data;
+ int channels;
+
+ dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params));
+ if (dmic->clk_div < 0) {
+ dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n",
+ dmic->out_freq, dmic->fclk_freq);
+ return -EINVAL;
+ }
+
+ dmic->ch_enabled = 0;
+ channels = params_channels(params);
+ switch (channels) {
+ case 6:
+ dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE;
+ /* fall through */
+ case 4:
+ dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE;
+ /* fall through */
+ case 2:
+ dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE;
+ break;
+ default:
+ dev_err(dmic->dev, "invalid number of legacy channels\n");
+ return -EINVAL;
+ }
+
+ /* packet size is threshold * channels */
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+ dma_data->maxburst = dmic->threshold * channels;
+ dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC /
+ params_rate(params);
+
+ return 0;
+}
+
+static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
+ u32 ctrl;
+
+ if (pm_qos_request_active(&dmic->pm_qos_req))
+ pm_qos_update_request(&dmic->pm_qos_req, dmic->latency);
+
+ /* Configure uplink threshold */
+ omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold);
+
+ ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG);
+
+ /* Set dmic out format */
+ ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK);
+ ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
+ OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
+
+ /* Configure dmic clock divider */
+ ctrl &= ~OMAP_DMIC_CLK_DIV_MASK;
+ ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div);
+
+ omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl);
+
+ omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG,
+ ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 |
+ OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3);
+
+ return 0;
+}
+
+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);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ omap_dmic_start(dmic);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ omap_dmic_stop(dmic);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id,
+ unsigned int freq)
+{
+ struct clk *parent_clk, *mux;
+ char *parent_clk_name;
+ int ret = 0;
+
+ switch (freq) {
+ case 12000000:
+ case 19200000:
+ case 24000000:
+ case 24576000:
+ break;
+ default:
+ dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq);
+ dmic->fclk_freq = 0;
+ return -EINVAL;
+ }
+
+ if (dmic->sysclk == clk_id) {
+ dmic->fclk_freq = freq;
+ return 0;
+ }
+
+ /* re-parent not allowed if a stream is ongoing */
+ if (dmic->active && dmic_is_enabled(dmic)) {
+ dev_err(dmic->dev, "can't re-parent when DMIC active\n");
+ return -EBUSY;
+ }
+
+ switch (clk_id) {
+ case OMAP_DMIC_SYSCLK_PAD_CLKS:
+ parent_clk_name = "pad_clks_ck";
+ break;
+ case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS:
+ parent_clk_name = "slimbus_clk";
+ break;
+ case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS:
+ parent_clk_name = "dmic_sync_mux_ck";
+ break;
+ default:
+ dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id);
+ return -EINVAL;
+ }
+
+ parent_clk = clk_get(dmic->dev, parent_clk_name);
+ if (IS_ERR(parent_clk)) {
+ dev_err(dmic->dev, "can't get %s\n", parent_clk_name);
+ return -ENODEV;
+ }
+
+ mux = clk_get_parent(dmic->fclk);
+ if (IS_ERR(mux)) {
+ dev_err(dmic->dev, "can't get fck mux parent\n");
+ clk_put(parent_clk);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dmic->mutex);
+ if (dmic->active) {
+ /* disable clock while reparenting */
+ pm_runtime_put_sync(dmic->dev);
+ ret = clk_set_parent(mux, parent_clk);
+ pm_runtime_get_sync(dmic->dev);
+ } else {
+ ret = clk_set_parent(mux, parent_clk);
+ }
+ mutex_unlock(&dmic->mutex);
+
+ if (ret < 0) {
+ dev_err(dmic->dev, "re-parent failed\n");
+ goto err_busy;
+ }
+
+ dmic->sysclk = clk_id;
+ dmic->fclk_freq = freq;
+
+err_busy:
+ clk_put(mux);
+ clk_put(parent_clk);
+
+ return ret;
+}
+
+static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id,
+ unsigned int freq)
+{
+ int ret = 0;
+
+ if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) {
+ dev_err(dmic->dev, "output clk_id (%d) not supported\n",
+ clk_id);
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 1536000:
+ case 2400000:
+ case 3072000:
+ case 3840000:
+ dmic->out_freq = freq;
+ break;
+ default:
+ dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq);
+ dmic->out_freq = 0;
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+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);
+
+ if (dir == SND_SOC_CLOCK_IN)
+ return omap_dmic_select_fclk(dmic, clk_id, freq);
+ else if (dir == SND_SOC_CLOCK_OUT)
+ return omap_dmic_select_outclk(dmic, clk_id, freq);
+
+ dev_err(dmic->dev, "invalid clock direction (%d)\n", dir);
+ return -EINVAL;
+}
+
+static const 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,
+ .prepare = omap_dmic_dai_prepare,
+ .trigger = omap_dmic_dai_trigger,
+ .set_sysclk = omap_dmic_set_dai_sysclk,
+};
+
+static int omap_dmic_probe(struct snd_soc_dai *dai)
+{
+ struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
+
+ pm_runtime_enable(dmic->dev);
+
+ /* Disable lines while request is ongoing */
+ pm_runtime_get_sync(dmic->dev);
+ omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00);
+ pm_runtime_put_sync(dmic->dev);
+
+ /* Configure DMIC threshold value */
+ dmic->threshold = OMAP_DMIC_THRES_MAX - 3;
+
+ snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data);
+
+ return 0;
+}
+
+static int omap_dmic_remove(struct snd_soc_dai *dai)
+{
+ struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai);
+
+ pm_runtime_disable(dmic->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver omap_dmic_dai = {
+ .name = "omap-dmic",
+ .probe = omap_dmic_probe,
+ .remove = omap_dmic_remove,
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 6,
+ .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
+ },
+ .ops = &omap_dmic_dai_ops,
+};
+
+static const struct snd_soc_component_driver omap_dmic_component = {
+ .name = "omap-dmic",
+};
+
+static int asoc_dmic_probe(struct platform_device *pdev)
+{
+ struct omap_dmic *dmic;
+ struct resource *res;
+ int ret;
+
+ dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL);
+ if (!dmic)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, dmic);
+ dmic->dev = &pdev->dev;
+ dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS;
+
+ mutex_init(&dmic->mutex);
+
+ dmic->fclk = devm_clk_get(dmic->dev, "fck");
+ if (IS_ERR(dmic->fclk)) {
+ dev_err(dmic->dev, "cant get fck\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
+ if (!res) {
+ dev_err(dmic->dev, "invalid dma memory resource\n");
+ return -ENODEV;
+ }
+ dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG;
+
+ dmic->dma_data.filter_data = "up_link";
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dmic->io_base))
+ return PTR_ERR(dmic->io_base);
+
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &omap_dmic_component,
+ &omap_dmic_dai, 1);
+ if (ret)
+ return ret;
+
+ ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link");
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct of_device_id omap_dmic_of_match[] = {
+ { .compatible = "ti,omap4-dmic", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
+
+static struct platform_driver asoc_dmic_driver = {
+ .driver = {
+ .name = "omap-dmic",
+ .of_match_table = omap_dmic_of_match,
+ },
+ .probe = asoc_dmic_probe,
+};
+
+module_platform_driver(asoc_dmic_driver);
+
+MODULE_ALIAS("platform:omap-dmic");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("OMAP DMIC ASoC Interface");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * omap-dmic.h -- OMAP Digital Microphone Controller
+ *
+ * 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
+
+#define OMAP_DMIC_REVISION_REG 0x00
+#define OMAP_DMIC_SYSCONFIG_REG 0x10
+#define OMAP_DMIC_IRQSTATUS_RAW_REG 0x24
+#define OMAP_DMIC_IRQSTATUS_REG 0x28
+#define OMAP_DMIC_IRQENABLE_SET_REG 0x2C
+#define OMAP_DMIC_IRQENABLE_CLR_REG 0x30
+#define OMAP_DMIC_IRQWAKE_EN_REG 0x34
+#define OMAP_DMIC_DMAENABLE_SET_REG 0x38
+#define OMAP_DMIC_DMAENABLE_CLR_REG 0x3C
+#define OMAP_DMIC_DMAWAKEEN_REG 0x40
+#define OMAP_DMIC_CTRL_REG 0x44
+#define OMAP_DMIC_DATA_REG 0x48
+#define OMAP_DMIC_FIFO_CTRL_REG 0x4C
+#define OMAP_DMIC_FIFO_DMIC1R_DATA_REG 0x50
+#define OMAP_DMIC_FIFO_DMIC1L_DATA_REG 0x54
+#define OMAP_DMIC_FIFO_DMIC2R_DATA_REG 0x58
+#define OMAP_DMIC_FIFO_DMIC2L_DATA_REG 0x5C
+#define OMAP_DMIC_FIFO_DMIC3R_DATA_REG 0x60
+#define OMAP_DMIC_FIFO_DMIC3L_DATA_REG 0x64
+
+/* IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR bit fields */
+#define OMAP_DMIC_IRQ (1 << 0)
+#define OMAP_DMIC_IRQ_FULL (1 << 1)
+#define OMAP_DMIC_IRQ_ALMST_EMPTY (1 << 2)
+#define OMAP_DMIC_IRQ_EMPTY (1 << 3)
+#define OMAP_DMIC_IRQ_MASK 0x07
+
+/* DMIC_DMAENABLE bit fields */
+#define OMAP_DMIC_DMA_ENABLE 0x1
+
+/* DMIC_CTRL bit fields */
+#define OMAP_DMIC_UP1_ENABLE (1 << 0)
+#define OMAP_DMIC_UP2_ENABLE (1 << 1)
+#define OMAP_DMIC_UP3_ENABLE (1 << 2)
+#define OMAP_DMIC_UP_ENABLE_MASK 0x7
+#define OMAP_DMIC_FORMAT (1 << 3)
+#define OMAP_DMIC_POLAR1 (1 << 4)
+#define OMAP_DMIC_POLAR2 (1 << 5)
+#define OMAP_DMIC_POLAR3 (1 << 6)
+#define OMAP_DMIC_POLAR_MASK (0x7 << 4)
+#define OMAP_DMIC_CLK_DIV(x) (((x) & 0x7) << 7)
+#define OMAP_DMIC_CLK_DIV_MASK (0x7 << 7)
+#define OMAP_DMIC_RESET (1 << 10)
+
+#define OMAP_DMICOUTFORMAT_LJUST (0 << 3)
+#define OMAP_DMICOUTFORMAT_RJUST (1 << 3)
+
+/* DMIC_FIFO_CTRL bit fields */
+#define OMAP_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 */
+ OMAP_DMIC_ABE_DMIC_CLK, /* abe_dmic_clk */
+};
+
+#endif
--- /dev/null
+/*
+ * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Jyri Sarha <jsarha@ti.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <uapi/sound/asound.h>
+#include <sound/asoundef.h>
+#include <sound/omap-hdmi-audio.h>
+
+#include "sdma-pcm.h"
+
+#define DRV_NAME "omap-hdmi-audio"
+
+struct hdmi_audio_data {
+ struct snd_soc_card *card;
+
+ const struct omap_hdmi_audio_ops *ops;
+ struct device *dssdev;
+ struct snd_dmaengine_dai_dma_data dma_data;
+ struct omap_dss_audio dss_audio;
+ struct snd_aes_iec958 iec;
+ struct snd_cea_861_aud_if cea;
+
+ struct mutex current_stream_lock;
+ struct snd_pcm_substream *current_stream;
+};
+
+static
+struct hdmi_audio_data *card_drvdata_substream(struct snd_pcm_substream *ss)
+{
+ struct snd_soc_pcm_runtime *rtd = ss->private_data;
+
+ return snd_soc_card_get_drvdata(rtd->card);
+}
+
+static void hdmi_dai_abort(struct device *dev)
+{
+ struct hdmi_audio_data *ad = dev_get_drvdata(dev);
+
+ mutex_lock(&ad->current_stream_lock);
+ if (ad->current_stream && ad->current_stream->runtime &&
+ snd_pcm_running(ad->current_stream)) {
+ dev_err(dev, "HDMI display disabled, aborting playback\n");
+ snd_pcm_stream_lock_irq(ad->current_stream);
+ snd_pcm_stop(ad->current_stream, SNDRV_PCM_STATE_DISCONNECTED);
+ snd_pcm_stream_unlock_irq(ad->current_stream);
+ }
+ mutex_unlock(&ad->current_stream_lock);
+}
+
+static int hdmi_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_audio_data *ad = card_drvdata_substream(substream);
+ int ret;
+ /*
+ * Make sure that the period bytes are multiple of the DMA packet size.
+ * Largest packet size we use is 32 32-bit words = 128 bytes
+ */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
+ if (ret < 0) {
+ dev_err(dai->dev, "Could not apply period constraint: %d\n",
+ ret);
+ return ret;
+ }
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128);
+ if (ret < 0) {
+ dev_err(dai->dev, "Could not apply buffer constraint: %d\n",
+ ret);
+ return ret;
+ }
+
+ snd_soc_dai_set_dma_data(dai, substream, &ad->dma_data);
+
+ mutex_lock(&ad->current_stream_lock);
+ ad->current_stream = substream;
+ mutex_unlock(&ad->current_stream_lock);
+
+ ret = ad->ops->audio_startup(ad->dssdev, hdmi_dai_abort);
+
+ if (ret) {
+ mutex_lock(&ad->current_stream_lock);
+ ad->current_stream = NULL;
+ mutex_unlock(&ad->current_stream_lock);
+ }
+
+ return ret;
+}
+
+static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_audio_data *ad = card_drvdata_substream(substream);
+ struct snd_aes_iec958 *iec = &ad->iec;
+ struct snd_cea_861_aud_if *cea = &ad->cea;
+
+ WARN_ON(ad->current_stream != substream);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ad->dma_data.maxburst = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ad->dma_data.maxburst = 32;
+ break;
+ default:
+ dev_err(dai->dev, "format not supported!\n");
+ return -EINVAL;
+ }
+
+ ad->dss_audio.iec = iec;
+ ad->dss_audio.cea = cea;
+ /*
+ * fill the IEC-60958 channel status word
+ */
+ /* initialize the word bytes */
+ memset(iec->status, 0, sizeof(iec->status));
+
+ /* specify IEC-60958-3 (commercial use) */
+ iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
+
+ /* specify that the audio is LPCM*/
+ iec->status[0] &= ~IEC958_AES0_NONAUDIO;
+
+ iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
+
+ iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
+
+ iec->status[1] = IEC958_AES1_CON_GENERAL;
+
+ iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
+
+ iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC;
+
+ switch (params_rate(params)) {
+ case 32000:
+ iec->status[3] |= IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ iec->status[3] |= IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ iec->status[3] |= IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ iec->status[3] |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ iec->status[3] |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ iec->status[3] |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ iec->status[3] |= IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ dev_err(dai->dev, "rate not supported!\n");
+ return -EINVAL;
+ }
+
+ /* specify the clock accuracy */
+ iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM;
+
+ /*
+ * specify the word length. The same word length value can mean
+ * two different lengths. Hence, we need to specify the maximum
+ * word length as well.
+ */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16;
+ iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20;
+ iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ default:
+ dev_err(dai->dev, "format not supported!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Fill the CEA-861 audio infoframe (see spec for details)
+ */
+
+ cea->db1_ct_cc = (params_channels(params) - 1)
+ & CEA861_AUDIO_INFOFRAME_DB1CC;
+ cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM;
+
+ cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM;
+ cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM;
+
+ cea->db3 = 0; /* not used, all zeros */
+
+ if (params_channels(params) == 2)
+ cea->db4_ca = 0x0;
+ else if (params_channels(params) == 6)
+ cea->db4_ca = 0xb;
+ else
+ cea->db4_ca = 0x13;
+
+ if (cea->db4_ca == 0x00)
+ cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED;
+ else
+ cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
+
+ /* the expression is trivial but makes clear what we are doing */
+ cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
+
+ return ad->ops->audio_config(ad->dssdev, &ad->dss_audio);
+}
+
+static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_audio_data *ad = card_drvdata_substream(substream);
+ int err = 0;
+
+ WARN_ON(ad->current_stream != substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ err = ad->ops->audio_start(ad->dssdev);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ad->ops->audio_stop(ad->dssdev);
+ break;
+ default:
+ err = -EINVAL;
+ }
+ return err;
+}
+
+static void hdmi_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct hdmi_audio_data *ad = card_drvdata_substream(substream);
+
+ WARN_ON(ad->current_stream != substream);
+
+ ad->ops->audio_shutdown(ad->dssdev);
+
+ mutex_lock(&ad->current_stream_lock);
+ ad->current_stream = NULL;
+ mutex_unlock(&ad->current_stream_lock);
+}
+
+static const struct snd_soc_dai_ops hdmi_dai_ops = {
+ .startup = hdmi_dai_startup,
+ .hw_params = hdmi_dai_hw_params,
+ .trigger = hdmi_dai_trigger,
+ .shutdown = hdmi_dai_shutdown,
+};
+
+static const struct snd_soc_component_driver omap_hdmi_component = {
+ .name = "omapdss_hdmi",
+};
+
+static struct snd_soc_dai_driver omap5_hdmi_dai = {
+ .name = "omap5-hdmi-dai",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &hdmi_dai_ops,
+};
+
+static struct snd_soc_dai_driver omap4_hdmi_dai = {
+ .name = "omap4-hdmi-dai",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000),
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &hdmi_dai_ops,
+};
+
+static int omap_hdmi_audio_probe(struct platform_device *pdev)
+{
+ struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct hdmi_audio_data *ad;
+ struct snd_soc_dai_driver *dai_drv;
+ struct snd_soc_card *card;
+ int ret;
+
+ if (!ha) {
+ dev_err(dev, "No platform data\n");
+ return -EINVAL;
+ }
+
+ ad = devm_kzalloc(dev, sizeof(*ad), GFP_KERNEL);
+ if (!ad)
+ return -ENOMEM;
+ ad->dssdev = ha->dev;
+ ad->ops = ha->ops;
+ ad->dma_data.addr = ha->audio_dma_addr;
+ ad->dma_data.filter_data = "audio_tx";
+ ad->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ mutex_init(&ad->current_stream_lock);
+
+ switch (ha->version) {
+ case 4:
+ dai_drv = &omap4_hdmi_dai;
+ break;
+ case 5:
+ dai_drv = &omap5_hdmi_dai;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = devm_snd_soc_register_component(ad->dssdev, &omap_hdmi_component,
+ dai_drv, 1);
+ if (ret)
+ return ret;
+
+ ret = sdma_pcm_platform_register(ad->dssdev, "audio_tx", NULL);
+ if (ret)
+ return ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ card->name = devm_kasprintf(dev, GFP_KERNEL,
+ "HDMI %s", dev_name(ad->dssdev));
+ if (!card->name)
+ return -ENOMEM;
+
+ card->owner = THIS_MODULE;
+ card->dai_link =
+ devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL);
+ if (!card->dai_link)
+ return -ENOMEM;
+ card->dai_link->name = card->name;
+ card->dai_link->stream_name = card->name;
+ card->dai_link->cpu_dai_name = dev_name(ad->dssdev);
+ card->dai_link->platform_name = dev_name(ad->dssdev);
+ card->dai_link->codec_name = "snd-soc-dummy";
+ card->dai_link->codec_dai_name = "snd-soc-dummy-dai";
+ card->num_links = 1;
+ card->dev = dev;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(dev, "snd_soc_register_card failed (%d)\n", ret);
+ return ret;
+ }
+
+ ad->card = card;
+ snd_soc_card_set_drvdata(card, ad);
+
+ dev_set_drvdata(dev, ad);
+
+ return 0;
+}
+
+static int omap_hdmi_audio_remove(struct platform_device *pdev)
+{
+ struct hdmi_audio_data *ad = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(ad->card);
+ return 0;
+}
+
+static struct platform_driver hdmi_audio_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = omap_hdmi_audio_probe,
+ .remove = omap_hdmi_audio_remove,
+};
+
+module_platform_driver(hdmi_audio_driver);
+
+MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
+MODULE_DESCRIPTION("OMAP HDMI Audio Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * OMAP Multi-Channel Buffered Serial Port
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#ifndef __OMAP_MCBSP_PRIV_H__
+#define __OMAP_MCBSP_PRIV_H__
+
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#ifdef CONFIG_ARCH_OMAP1
+#define mcbsp_omap1() 1
+#else
+#define mcbsp_omap1() 0
+#endif
+
+/* McBSP register numbers. Register address offset = num * reg_step */
+enum {
+ /* Common registers */
+ OMAP_MCBSP_REG_SPCR2 = 4,
+ OMAP_MCBSP_REG_SPCR1,
+ OMAP_MCBSP_REG_RCR2,
+ OMAP_MCBSP_REG_RCR1,
+ OMAP_MCBSP_REG_XCR2,
+ OMAP_MCBSP_REG_XCR1,
+ OMAP_MCBSP_REG_SRGR2,
+ OMAP_MCBSP_REG_SRGR1,
+ OMAP_MCBSP_REG_MCR2,
+ OMAP_MCBSP_REG_MCR1,
+ OMAP_MCBSP_REG_RCERA,
+ OMAP_MCBSP_REG_RCERB,
+ OMAP_MCBSP_REG_XCERA,
+ OMAP_MCBSP_REG_XCERB,
+ OMAP_MCBSP_REG_PCR0,
+ OMAP_MCBSP_REG_RCERC,
+ OMAP_MCBSP_REG_RCERD,
+ OMAP_MCBSP_REG_XCERC,
+ OMAP_MCBSP_REG_XCERD,
+ OMAP_MCBSP_REG_RCERE,
+ OMAP_MCBSP_REG_RCERF,
+ OMAP_MCBSP_REG_XCERE,
+ OMAP_MCBSP_REG_XCERF,
+ OMAP_MCBSP_REG_RCERG,
+ OMAP_MCBSP_REG_RCERH,
+ OMAP_MCBSP_REG_XCERG,
+ OMAP_MCBSP_REG_XCERH,
+
+ /* OMAP1-OMAP2420 registers */
+ OMAP_MCBSP_REG_DRR2 = 0,
+ OMAP_MCBSP_REG_DRR1,
+ OMAP_MCBSP_REG_DXR2,
+ OMAP_MCBSP_REG_DXR1,
+
+ /* OMAP2430 and onwards */
+ OMAP_MCBSP_REG_DRR = 0,
+ OMAP_MCBSP_REG_DXR = 2,
+ OMAP_MCBSP_REG_SYSCON = 35,
+ OMAP_MCBSP_REG_THRSH2,
+ OMAP_MCBSP_REG_THRSH1,
+ OMAP_MCBSP_REG_IRQST = 40,
+ OMAP_MCBSP_REG_IRQEN,
+ OMAP_MCBSP_REG_WAKEUPEN,
+ OMAP_MCBSP_REG_XCCR,
+ OMAP_MCBSP_REG_RCCR,
+ OMAP_MCBSP_REG_XBUFFSTAT,
+ OMAP_MCBSP_REG_RBUFFSTAT,
+ OMAP_MCBSP_REG_SSELCR,
+};
+
+/************************** McBSP SPCR1 bit definitions ***********************/
+#define RRST BIT(0)
+#define RRDY BIT(1)
+#define RFULL BIT(2)
+#define RSYNC_ERR BIT(3)
+#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */
+#define ABIS BIT(6)
+#define DXENA BIT(7)
+#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */
+#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */
+#define ALB BIT(15)
+#define DLB BIT(15)
+
+/************************** McBSP SPCR2 bit definitions ***********************/
+#define XRST BIT(0)
+#define XRDY BIT(1)
+#define XEMPTY BIT(2)
+#define XSYNC_ERR BIT(3)
+#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */
+#define GRST BIT(6)
+#define FRST BIT(7)
+#define SOFT BIT(8)
+#define FREE BIT(9)
+
+/************************** McBSP PCR bit definitions *************************/
+#define CLKRP BIT(0)
+#define CLKXP BIT(1)
+#define FSRP BIT(2)
+#define FSXP BIT(3)
+#define DR_STAT BIT(4)
+#define DX_STAT BIT(5)
+#define CLKS_STAT BIT(6)
+#define SCLKME BIT(7)
+#define CLKRM BIT(8)
+#define CLKXM BIT(9)
+#define FSRM BIT(10)
+#define FSXM BIT(11)
+#define RIOEN BIT(12)
+#define XIOEN BIT(13)
+#define IDLE_EN BIT(14)
+
+/************************** McBSP RCR1 bit definitions ************************/
+#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */
+#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
+
+/************************** McBSP XCR1 bit definitions ************************/
+#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */
+#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
+
+/*************************** McBSP RCR2 bit definitions ***********************/
+#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */
+#define RFIG BIT(2)
+#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */
+#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */
+#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
+#define RPHASE BIT(15)
+
+/*************************** McBSP XCR2 bit definitions ***********************/
+#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */
+#define XFIG BIT(2)
+#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */
+#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */
+#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */
+#define XPHASE BIT(15)
+
+/************************* McBSP SRGR1 bit definitions ************************/
+#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */
+#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */
+
+/************************* McBSP SRGR2 bit definitions ************************/
+#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */
+#define FSGM BIT(12)
+#define CLKSM BIT(13)
+#define CLKSP BIT(14)
+#define GSYNC BIT(15)
+
+/************************* McBSP MCR1 bit definitions *************************/
+#define RMCM BIT(0)
+#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */
+#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */
+#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */
+
+/************************* McBSP MCR2 bit definitions *************************/
+#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */
+#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */
+#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */
+#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */
+
+/*********************** McBSP XCCR bit definitions *************************/
+#define XDISABLE BIT(0)
+#define XDMAEN BIT(3)
+#define DILB BIT(5)
+#define XFULL_CYCLE BIT(11)
+#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */
+#define PPCONNECT BIT(14)
+#define EXTCLKGATE BIT(15)
+
+/********************** McBSP RCCR bit definitions *************************/
+#define RDISABLE BIT(0)
+#define RDMAEN BIT(3)
+#define RFULL_CYCLE BIT(11)
+
+/********************** McBSP SYSCONFIG bit definitions ********************/
+#define SOFTRST BIT(1)
+#define ENAWAKEUP BIT(2)
+#define SIDLEMODE(value) (((value) & 0x3) << 3)
+#define CLOCKACTIVITY(value) (((value) & 0x3) << 8)
+
+/********************** McBSP DMA operating modes **************************/
+#define MCBSP_DMA_MODE_ELEMENT 0
+#define MCBSP_DMA_MODE_THRESHOLD 1
+
+/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/
+#define RSYNCERREN BIT(0)
+#define RFSREN BIT(1)
+#define REOFEN BIT(2)
+#define RRDYEN BIT(3)
+#define RUNDFLEN BIT(4)
+#define ROVFLEN BIT(5)
+#define XSYNCERREN BIT(7)
+#define XFSXEN BIT(8)
+#define XEOFEN BIT(9)
+#define XRDYEN BIT(10)
+#define XUNDFLEN BIT(11)
+#define XOVFLEN BIT(12)
+#define XEMPTYEOFEN BIT(14)
+
+/* Clock signal muxing options */
+#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */
+#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */
+#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */
+#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */
+
+/* McBSP functional clock sources */
+#define MCBSP_CLKS_PRCM_SRC 0
+#define MCBSP_CLKS_PAD_SRC 1
+
+/* we don't do multichannel for now */
+struct omap_mcbsp_reg_cfg {
+ u16 spcr2;
+ u16 spcr1;
+ u16 rcr2;
+ u16 rcr1;
+ u16 xcr2;
+ u16 xcr1;
+ u16 srgr2;
+ u16 srgr1;
+ u16 mcr2;
+ u16 mcr1;
+ u16 pcr0;
+ u16 rcerc;
+ u16 rcerd;
+ u16 xcerc;
+ u16 xcerd;
+ u16 rcere;
+ u16 rcerf;
+ u16 xcere;
+ u16 xcerf;
+ u16 rcerg;
+ u16 rcerh;
+ u16 xcerg;
+ u16 xcerh;
+ u16 xccr;
+ u16 rccr;
+};
+
+struct omap_mcbsp_st_data;
+
+struct omap_mcbsp {
+ struct device *dev;
+ struct clk *fclk;
+ spinlock_t lock;
+ unsigned long phys_base;
+ unsigned long phys_dma_base;
+ void __iomem *io_base;
+ u8 id;
+ /*
+ * Flags indicating is the bus already activated and configured by
+ * another substream
+ */
+ int active;
+ int configured;
+ u8 free;
+
+ int irq;
+ int rx_irq;
+ int tx_irq;
+
+ /* Protect the field .free, while checking if the mcbsp is in use */
+ struct omap_mcbsp_platform_data *pdata;
+ struct omap_mcbsp_st_data *st_data;
+ struct omap_mcbsp_reg_cfg cfg_regs;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ unsigned int dma_req[2];
+ int dma_op_mode;
+ u16 max_tx_thres;
+ u16 max_rx_thres;
+ void *reg_cache;
+ int reg_cache_size;
+
+ unsigned int fmt;
+ unsigned int in_freq;
+ unsigned int latency[2];
+ int clk_div;
+ int wlen;
+
+ struct pm_qos_request pm_qos_req;
+};
+
+static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
+{
+ void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
+
+ if (mcbsp->pdata->reg_size == 2) {
+ ((u16 *)mcbsp->reg_cache)[reg] = (u16)val;
+ writew_relaxed((u16)val, addr);
+ } else {
+ ((u32 *)mcbsp->reg_cache)[reg] = val;
+ writel_relaxed(val, addr);
+ }
+}
+
+static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg,
+ bool from_cache)
+{
+ void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
+
+ if (mcbsp->pdata->reg_size == 2) {
+ return !from_cache ? readw_relaxed(addr) :
+ ((u16 *)mcbsp->reg_cache)[reg];
+ } else {
+ return !from_cache ? readl_relaxed(addr) :
+ ((u32 *)mcbsp->reg_cache)[reg];
+ }
+}
+
+#define MCBSP_READ(mcbsp, reg) \
+ omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
+#define MCBSP_WRITE(mcbsp, reg, val) \
+ omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
+#define MCBSP_READ_CACHE(mcbsp, reg) \
+ omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
+
+
+/* Sidetone specific API */
+int omap_mcbsp_st_init(struct platform_device *pdev);
+void omap_mcbsp_st_cleanup(struct platform_device *pdev);
+
+int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp);
+int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp);
+
+#endif /* __OMAP_MCBSP_PRIV_H__ */
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * McBSP Sidetone support
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#include "omap-mcbsp.h"
+#include "omap-mcbsp-priv.h"
+
+/* OMAP3 sidetone control registers */
+#define OMAP_ST_REG_REV 0x00
+#define OMAP_ST_REG_SYSCONFIG 0x10
+#define OMAP_ST_REG_IRQSTATUS 0x18
+#define OMAP_ST_REG_IRQENABLE 0x1C
+#define OMAP_ST_REG_SGAINCR 0x24
+#define OMAP_ST_REG_SFIRCR 0x28
+#define OMAP_ST_REG_SSELCR 0x2C
+
+/********************** McBSP SSELCR bit definitions ***********************/
+#define SIDETONEEN BIT(10)
+
+/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/
+#define ST_AUTOIDLE BIT(0)
+
+/********************** McBSP Sidetone SGAINCR bit definitions *************/
+#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */
+#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */
+
+/********************** McBSP Sidetone SFIRCR bit definitions **************/
+#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */
+
+/********************** McBSP Sidetone SSELCR bit definitions **************/
+#define ST_SIDETONEEN BIT(0)
+#define ST_COEFFWREN BIT(1)
+#define ST_COEFFWRDONE BIT(2)
+
+struct omap_mcbsp_st_data {
+ void __iomem *io_base_st;
+ struct clk *mcbsp_iclk;
+ bool running;
+ bool enabled;
+ s16 taps[128]; /* Sidetone filter coefficients */
+ int nr_taps; /* Number of filter coefficients in use */
+ s16 ch0gain;
+ s16 ch1gain;
+};
+
+static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
+{
+ writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
+}
+
+static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
+{
+ return readl_relaxed(mcbsp->st_data->io_base_st + reg);
+}
+
+#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
+#define MCBSP_ST_WRITE(mcbsp, reg, val) \
+ omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
+
+static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp)
+{
+ unsigned int w;
+
+ if (mcbsp->pdata->force_ick_on)
+ mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true);
+
+ /* Disable Sidetone clock auto-gating for normal operation */
+ w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
+ MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
+
+ /* Enable McBSP Sidetone */
+ w = MCBSP_READ(mcbsp, SSELCR);
+ MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
+
+ /* Enable Sidetone from Sidetone Core */
+ w = MCBSP_ST_READ(mcbsp, SSELCR);
+ MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
+}
+
+static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp)
+{
+ unsigned int w;
+
+ w = MCBSP_ST_READ(mcbsp, SSELCR);
+ MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
+
+ w = MCBSP_READ(mcbsp, SSELCR);
+ MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
+
+ /* Enable Sidetone clock auto-gating to reduce power consumption */
+ w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
+ MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
+
+ if (mcbsp->pdata->force_ick_on)
+ mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false);
+}
+
+static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
+{
+ u16 val, i;
+
+ val = MCBSP_ST_READ(mcbsp, SSELCR);
+
+ if (val & ST_COEFFWREN)
+ MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
+
+ MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
+
+ for (i = 0; i < 128; i++)
+ MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
+
+ i = 0;
+
+ val = MCBSP_ST_READ(mcbsp, SSELCR);
+ while (!(val & ST_COEFFWRDONE) && (++i < 1000))
+ val = MCBSP_ST_READ(mcbsp, SSELCR);
+
+ MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
+
+ if (i == 1000)
+ dev_err(mcbsp->dev, "McBSP FIR load error!\n");
+}
+
+static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp)
+{
+ u16 w;
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ w = MCBSP_ST_READ(mcbsp, SSELCR);
+
+ MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) |
+ ST_CH1GAIN(st_data->ch1gain));
+}
+
+static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel,
+ s16 chgain)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ int ret = 0;
+
+ if (!st_data)
+ return -ENOENT;
+
+ spin_lock_irq(&mcbsp->lock);
+ if (channel == 0)
+ st_data->ch0gain = chgain;
+ else if (channel == 1)
+ st_data->ch1gain = chgain;
+ else
+ ret = -EINVAL;
+
+ if (st_data->enabled)
+ omap_mcbsp_st_chgain(mcbsp);
+ spin_unlock_irq(&mcbsp->lock);
+
+ return ret;
+}
+
+static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel,
+ s16 *chgain)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ int ret = 0;
+
+ if (!st_data)
+ return -ENOENT;
+
+ spin_lock_irq(&mcbsp->lock);
+ if (channel == 0)
+ *chgain = st_data->ch0gain;
+ else if (channel == 1)
+ *chgain = st_data->ch1gain;
+ else
+ ret = -EINVAL;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return ret;
+}
+
+static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENODEV;
+
+ spin_lock_irq(&mcbsp->lock);
+ st_data->enabled = 1;
+ omap_mcbsp_st_start(mcbsp);
+ spin_unlock_irq(&mcbsp->lock);
+
+ return 0;
+}
+
+static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ int ret = 0;
+
+ if (!st_data)
+ return -ENODEV;
+
+ spin_lock_irq(&mcbsp->lock);
+ omap_mcbsp_st_stop(mcbsp);
+ st_data->enabled = 0;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return ret;
+}
+
+static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENODEV;
+
+ return st_data->enabled;
+}
+
+static ssize_t st_taps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ ssize_t status = 0;
+ int i;
+
+ spin_lock_irq(&mcbsp->lock);
+ for (i = 0; i < st_data->nr_taps; i++)
+ status += sprintf(&buf[status], (i ? ", %d" : "%d"),
+ st_data->taps[i]);
+ if (i)
+ status += sprintf(&buf[status], "\n");
+ spin_unlock_irq(&mcbsp->lock);
+
+ return status;
+}
+
+static ssize_t st_taps_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ int val, tmp, status, i = 0;
+
+ spin_lock_irq(&mcbsp->lock);
+ memset(st_data->taps, 0, sizeof(st_data->taps));
+ st_data->nr_taps = 0;
+
+ do {
+ status = sscanf(buf, "%d%n", &val, &tmp);
+ if (status < 0 || status == 0) {
+ size = -EINVAL;
+ goto out;
+ }
+ if (val < -32768 || val > 32767) {
+ size = -EINVAL;
+ goto out;
+ }
+ st_data->taps[i++] = val;
+ buf += tmp;
+ if (*buf != ',')
+ break;
+ buf++;
+ } while (1);
+
+ st_data->nr_taps = i;
+
+out:
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR_RW(st_taps);
+
+static const struct attribute *sidetone_attrs[] = {
+ &dev_attr_st_taps.attr,
+ NULL,
+};
+
+static const struct attribute_group sidetone_attr_group = {
+ .attrs = (struct attribute **)sidetone_attrs,
+};
+
+int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (st_data->enabled && !st_data->running) {
+ omap_mcbsp_st_fir_write(mcbsp, st_data->taps);
+ omap_mcbsp_st_chgain(mcbsp);
+
+ if (!mcbsp->free) {
+ omap_mcbsp_st_on(mcbsp);
+ st_data->running = 1;
+ }
+ }
+
+ return 0;
+}
+
+int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (st_data->running) {
+ if (!mcbsp->free) {
+ omap_mcbsp_st_off(mcbsp);
+ st_data->running = 0;
+ }
+ }
+
+ return 0;
+}
+
+int omap_mcbsp_st_init(struct platform_device *pdev)
+{
+ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+ struct omap_mcbsp_st_data *st_data;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
+ if (!res)
+ return 0;
+
+ st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
+ if (!st_data)
+ return -ENOMEM;
+
+ st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick");
+ if (IS_ERR(st_data->mcbsp_iclk)) {
+ dev_warn(mcbsp->dev,
+ "Failed to get ick, sidetone might be broken\n");
+ st_data->mcbsp_iclk = NULL;
+ }
+
+ st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
+ resource_size(res));
+ if (!st_data->io_base_st)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
+ if (ret)
+ return ret;
+
+ mcbsp->st_data = st_data;
+
+ return 0;
+}
+
+void omap_mcbsp_st_cleanup(struct platform_device *pdev)
+{
+ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+
+ if (mcbsp->st_data) {
+ sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
+ clk_put(mcbsp->st_data->mcbsp_iclk);
+ }
+}
+
+static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int max = mc->max;
+ int min = mc->min;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = min;
+ uinfo->value.integer.max = max;
+ return 0;
+}
+
+#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \
+static int \
+omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
+ struct snd_ctl_elem_value *uc) \
+{ \
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
+ struct soc_mixer_control *mc = \
+ (struct soc_mixer_control *)kc->private_value; \
+ int max = mc->max; \
+ int min = mc->min; \
+ int val = uc->value.integer.value[0]; \
+ \
+ if (val < min || val > max) \
+ return -EINVAL; \
+ \
+ /* OMAP McBSP implementation uses index values 0..4 */ \
+ return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \
+} \
+ \
+static int \
+omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
+ struct snd_ctl_elem_value *uc) \
+{ \
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \
+ s16 chgain; \
+ \
+ if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \
+ return -EAGAIN; \
+ \
+ uc->value.integer.value[0] = chgain; \
+ return 0; \
+}
+
+OMAP_MCBSP_ST_CHANNEL_VOLUME(0)
+OMAP_MCBSP_ST_CHANNEL_VOLUME(1)
+
+static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ u8 value = ucontrol->value.integer.value[0];
+
+ if (value == omap_mcbsp_st_is_enabled(mcbsp))
+ return 0;
+
+ if (value)
+ omap_mcbsp_st_enable(mcbsp);
+ else
+ omap_mcbsp_st_disable(mcbsp);
+
+ return 1;
+}
+
+static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+
+ ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp);
+ return 0;
+}
+
+#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
+ xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = omap_mcbsp_st_info_volsw, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct soc_mixer_control) \
+ {.min = xmin, .max = xmax} }
+
+#define OMAP_MCBSP_ST_CONTROLS(port) \
+static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
+SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \
+ omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \
+OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
+ -32768, 32767, \
+ omap_mcbsp_get_st_ch0_volume, \
+ omap_mcbsp_set_st_ch0_volume), \
+OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
+ -32768, 32767, \
+ omap_mcbsp_get_st_ch1_volume, \
+ omap_mcbsp_set_st_ch1_volume), \
+}
+
+OMAP_MCBSP_ST_CONTROLS(2);
+OMAP_MCBSP_ST_CONTROLS(3);
+
+int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
+{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if (!mcbsp->st_data) {
+ dev_warn(mcbsp->dev, "No sidetone data for port\n");
+ return 0;
+ }
+
+ switch (port_id) {
+ case 2: /* McBSP 2 */
+ return snd_soc_add_dai_controls(cpu_dai,
+ omap_mcbsp2_st_controls,
+ ARRAY_SIZE(omap_mcbsp2_st_controls));
+ case 3: /* McBSP 3 */
+ return snd_soc_add_dai_controls(cpu_dai,
+ omap_mcbsp3_st_controls,
+ ARRAY_SIZE(omap_mcbsp3_st_controls));
+ default:
+ dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
--- /dev/null
+/*
+ * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "omap-mcbsp-priv.h"
+#include "omap-mcbsp.h"
+#include "sdma-pcm.h"
+
+#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
+
+enum {
+ OMAP_MCBSP_WORD_8 = 0,
+ OMAP_MCBSP_WORD_12,
+ OMAP_MCBSP_WORD_16,
+ OMAP_MCBSP_WORD_20,
+ OMAP_MCBSP_WORD_24,
+ OMAP_MCBSP_WORD_32,
+};
+
+static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
+{
+ dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
+ dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2));
+ dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1));
+ dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2));
+ dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1));
+ dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2));
+ dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1));
+ dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2));
+ dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1));
+ dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2));
+ dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1));
+ dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2));
+ dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1));
+ dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0));
+ dev_dbg(mcbsp->dev, "***********************\n");
+}
+
+static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
+{
+ struct clk *fck_src;
+ const char *src;
+ int r;
+
+ if (fck_src_id == MCBSP_CLKS_PAD_SRC)
+ src = "pad_fck";
+ else if (fck_src_id == MCBSP_CLKS_PRCM_SRC)
+ src = "prcm_fck";
+ else
+ return -EINVAL;
+
+ fck_src = clk_get(mcbsp->dev, src);
+ if (IS_ERR(fck_src)) {
+ dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
+ return -EINVAL;
+ }
+
+ pm_runtime_put_sync(mcbsp->dev);
+
+ r = clk_set_parent(mcbsp->fclk, fck_src);
+ if (r) {
+ dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n",
+ src);
+ clk_put(fck_src);
+ return r;
+ }
+
+ pm_runtime_get_sync(mcbsp->dev);
+
+ clk_put(fck_src);
+
+ return 0;
+}
+
+static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data)
+{
+ struct omap_mcbsp *mcbsp = data;
+ u16 irqst;
+
+ irqst = MCBSP_READ(mcbsp, IRQST);
+ dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
+
+ if (irqst & RSYNCERREN)
+ dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
+ if (irqst & RFSREN)
+ dev_dbg(mcbsp->dev, "RX Frame Sync\n");
+ if (irqst & REOFEN)
+ dev_dbg(mcbsp->dev, "RX End Of Frame\n");
+ if (irqst & RRDYEN)
+ dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
+ if (irqst & RUNDFLEN)
+ dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
+ if (irqst & ROVFLEN)
+ dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
+
+ if (irqst & XSYNCERREN)
+ dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
+ if (irqst & XFSXEN)
+ dev_dbg(mcbsp->dev, "TX Frame Sync\n");
+ if (irqst & XEOFEN)
+ dev_dbg(mcbsp->dev, "TX End Of Frame\n");
+ if (irqst & XRDYEN)
+ dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
+ if (irqst & XUNDFLEN)
+ dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
+ if (irqst & XOVFLEN)
+ dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
+ if (irqst & XEMPTYEOFEN)
+ dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
+
+ MCBSP_WRITE(mcbsp, IRQST, irqst);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data)
+{
+ struct omap_mcbsp *mcbsp = data;
+ u16 irqst_spcr2;
+
+ irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2);
+ dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
+
+ if (irqst_spcr2 & XSYNC_ERR) {
+ dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n",
+ irqst_spcr2);
+ /* Writing zero to XSYNC_ERR clears the IRQ */
+ MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data)
+{
+ struct omap_mcbsp *mcbsp = data;
+ u16 irqst_spcr1;
+
+ irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1);
+ dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
+
+ if (irqst_spcr1 & RSYNC_ERR) {
+ dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n",
+ irqst_spcr1);
+ /* Writing zero to RSYNC_ERR clears the IRQ */
+ MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1));
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * omap_mcbsp_config simply write a config to the
+ * appropriate McBSP.
+ * You either call this function or set the McBSP registers
+ * by yourself before calling omap_mcbsp_start().
+ */
+static void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
+ const struct omap_mcbsp_reg_cfg *config)
+{
+ dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
+ mcbsp->id, mcbsp->phys_base);
+
+ /* We write the given config */
+ MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
+ MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
+ MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
+ MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
+ MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
+ MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
+ MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
+ MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
+ MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
+ MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
+ MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
+ if (mcbsp->pdata->has_ccr) {
+ MCBSP_WRITE(mcbsp, XCCR, config->xccr);
+ MCBSP_WRITE(mcbsp, RCCR, config->rccr);
+ }
+ /* Enable wakeup behavior */
+ if (mcbsp->pdata->has_wakeup)
+ MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
+
+ /* Enable TX/RX sync error interrupts by default */
+ if (mcbsp->irq)
+ MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN |
+ RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN);
+}
+
+/**
+ * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register
+ * @mcbsp: omap_mcbsp struct for the McBSP instance
+ * @stream: Stream direction (playback/capture)
+ *
+ * Returns the address of mcbsp data transmit register or data receive register
+ * to be used by DMA for transferring/receiving data
+ */
+static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp,
+ unsigned int stream)
+{
+ int data_reg;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (mcbsp->pdata->reg_size == 2)
+ data_reg = OMAP_MCBSP_REG_DXR1;
+ else
+ data_reg = OMAP_MCBSP_REG_DXR;
+ } else {
+ if (mcbsp->pdata->reg_size == 2)
+ data_reg = OMAP_MCBSP_REG_DRR1;
+ else
+ data_reg = OMAP_MCBSP_REG_DRR;
+ }
+
+ return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step;
+}
+
+/*
+ * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
+ * The threshold parameter is 1 based, and it is converted (threshold - 1)
+ * for the THRSH2 register.
+ */
+static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
+{
+ if (threshold && threshold <= mcbsp->max_tx_thres)
+ MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
+}
+
+/*
+ * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
+ * The threshold parameter is 1 based, and it is converted (threshold - 1)
+ * for the THRSH1 register.
+ */
+static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
+{
+ if (threshold && threshold <= mcbsp->max_rx_thres)
+ MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
+}
+
+/*
+ * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
+ */
+static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp)
+{
+ u16 buffstat;
+
+ /* Returns the number of free locations in the buffer */
+ buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
+
+ /* Number of slots are different in McBSP ports */
+ return mcbsp->pdata->buffer_size - buffstat;
+}
+
+/*
+ * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
+ * to reach the threshold value (when the DMA will be triggered to read it)
+ */
+static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp)
+{
+ u16 buffstat, threshold;
+
+ /* Returns the number of used locations in the buffer */
+ buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
+ /* RX threshold */
+ threshold = MCBSP_READ(mcbsp, THRSH1);
+
+ /* Return the number of location till we reach the threshold limit */
+ if (threshold <= buffstat)
+ return 0;
+ else
+ return threshold - buffstat;
+}
+
+static int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
+{
+ void *reg_cache;
+ int err;
+
+ reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL);
+ if (!reg_cache)
+ return -ENOMEM;
+
+ spin_lock(&mcbsp->lock);
+ if (!mcbsp->free) {
+ dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id);
+ err = -EBUSY;
+ goto err_kfree;
+ }
+
+ mcbsp->free = false;
+ mcbsp->reg_cache = reg_cache;
+ spin_unlock(&mcbsp->lock);
+
+ if(mcbsp->pdata->ops && mcbsp->pdata->ops->request)
+ mcbsp->pdata->ops->request(mcbsp->id - 1);
+
+ /*
+ * Make sure that transmitter, receiver and sample-rate generator are
+ * not running before activating IRQs.
+ */
+ MCBSP_WRITE(mcbsp, SPCR1, 0);
+ MCBSP_WRITE(mcbsp, SPCR2, 0);
+
+ if (mcbsp->irq) {
+ err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
+ "McBSP", (void *)mcbsp);
+ if (err != 0) {
+ dev_err(mcbsp->dev, "Unable to request IRQ\n");
+ goto err_clk_disable;
+ }
+ } else {
+ err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
+ "McBSP TX", (void *)mcbsp);
+ if (err != 0) {
+ dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
+ goto err_clk_disable;
+ }
+
+ err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
+ "McBSP RX", (void *)mcbsp);
+ if (err != 0) {
+ dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
+ goto err_free_irq;
+ }
+ }
+
+ return 0;
+err_free_irq:
+ free_irq(mcbsp->tx_irq, (void *)mcbsp);
+err_clk_disable:
+ if(mcbsp->pdata->ops && mcbsp->pdata->ops->free)
+ mcbsp->pdata->ops->free(mcbsp->id - 1);
+
+ /* Disable wakeup behavior */
+ if (mcbsp->pdata->has_wakeup)
+ MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
+
+ spin_lock(&mcbsp->lock);
+ mcbsp->free = true;
+ mcbsp->reg_cache = NULL;
+err_kfree:
+ spin_unlock(&mcbsp->lock);
+ kfree(reg_cache);
+
+ return err;
+}
+
+static void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
+{
+ void *reg_cache;
+
+ if(mcbsp->pdata->ops && mcbsp->pdata->ops->free)
+ mcbsp->pdata->ops->free(mcbsp->id - 1);
+
+ /* Disable wakeup behavior */
+ if (mcbsp->pdata->has_wakeup)
+ MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
+
+ /* Disable interrupt requests */
+ if (mcbsp->irq)
+ MCBSP_WRITE(mcbsp, IRQEN, 0);
+
+ if (mcbsp->irq) {
+ free_irq(mcbsp->irq, (void *)mcbsp);
+ } else {
+ free_irq(mcbsp->rx_irq, (void *)mcbsp);
+ free_irq(mcbsp->tx_irq, (void *)mcbsp);
+ }
+
+ reg_cache = mcbsp->reg_cache;
+
+ /*
+ * Select CLKS source from internal source unconditionally before
+ * marking the McBSP port as free.
+ * If the external clock source via MCBSP_CLKS pin has been selected the
+ * system will refuse to enter idle if the CLKS pin source is not reset
+ * back to internal source.
+ */
+ if (!mcbsp_omap1())
+ omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC);
+
+ spin_lock(&mcbsp->lock);
+ if (mcbsp->free)
+ dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
+ else
+ mcbsp->free = true;
+ mcbsp->reg_cache = NULL;
+ spin_unlock(&mcbsp->lock);
+
+ kfree(reg_cache);
+}
+
+/*
+ * Here we start the McBSP, by enabling transmitter, receiver or both.
+ * If no transmitter or receiver is active prior calling, then sample-rate
+ * generator and frame sync are started.
+ */
+static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream)
+{
+ int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int rx = !tx;
+ int enable_srg = 0;
+ u16 w;
+
+ if (mcbsp->st_data)
+ omap_mcbsp_st_start(mcbsp);
+
+ /* Only enable SRG, if McBSP is master */
+ w = MCBSP_READ_CACHE(mcbsp, PCR0);
+ if (w & (FSXM | FSRM | CLKXM | CLKRM))
+ enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
+ MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
+
+ if (enable_srg) {
+ /* Start the sample generator */
+ w = MCBSP_READ_CACHE(mcbsp, SPCR2);
+ MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
+ }
+
+ /* Enable transmitter and receiver */
+ tx &= 1;
+ w = MCBSP_READ_CACHE(mcbsp, SPCR2);
+ MCBSP_WRITE(mcbsp, SPCR2, w | tx);
+
+ rx &= 1;
+ w = MCBSP_READ_CACHE(mcbsp, SPCR1);
+ MCBSP_WRITE(mcbsp, SPCR1, w | rx);
+
+ /*
+ * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
+ * REVISIT: 100us may give enough time for two CLKSRG, however
+ * due to some unknown PM related, clock gating etc. reason it
+ * is now at 500us.
+ */
+ udelay(500);
+
+ if (enable_srg) {
+ /* Start frame sync */
+ w = MCBSP_READ_CACHE(mcbsp, SPCR2);
+ MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
+ }
+
+ if (mcbsp->pdata->has_ccr) {
+ /* Release the transmitter and receiver */
+ w = MCBSP_READ_CACHE(mcbsp, XCCR);
+ w &= ~(tx ? XDISABLE : 0);
+ MCBSP_WRITE(mcbsp, XCCR, w);
+ w = MCBSP_READ_CACHE(mcbsp, RCCR);
+ w &= ~(rx ? RDISABLE : 0);
+ MCBSP_WRITE(mcbsp, RCCR, w);
+ }
+
+ /* Dump McBSP Regs */
+ omap_mcbsp_dump_reg(mcbsp);
+}
+
+static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream)
+{
+ int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int rx = !tx;
+ int idle;
+ u16 w;
+
+ /* Reset transmitter */
+ tx &= 1;
+ if (mcbsp->pdata->has_ccr) {
+ w = MCBSP_READ_CACHE(mcbsp, XCCR);
+ w |= (tx ? XDISABLE : 0);
+ MCBSP_WRITE(mcbsp, XCCR, w);
+ }
+ w = MCBSP_READ_CACHE(mcbsp, SPCR2);
+ MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
+
+ /* Reset receiver */
+ rx &= 1;
+ if (mcbsp->pdata->has_ccr) {
+ w = MCBSP_READ_CACHE(mcbsp, RCCR);
+ w |= (rx ? RDISABLE : 0);
+ MCBSP_WRITE(mcbsp, RCCR, w);
+ }
+ w = MCBSP_READ_CACHE(mcbsp, SPCR1);
+ MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
+
+ idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
+ MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
+
+ if (idle) {
+ /* Reset the sample rate generator */
+ w = MCBSP_READ_CACHE(mcbsp, SPCR2);
+ MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
+ }
+
+ if (mcbsp->st_data)
+ omap_mcbsp_st_stop(mcbsp);
+}
+
+#define max_thres(m) (mcbsp->pdata->buffer_size)
+#define valid_threshold(m, val) ((val) <= max_thres(m))
+#define THRESHOLD_PROP_BUILDER(prop) \
+static ssize_t prop##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%u\n", mcbsp->prop); \
+} \
+ \
+static ssize_t prop##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t size) \
+{ \
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int status; \
+ \
+ status = kstrtoul(buf, 0, &val); \
+ if (status) \
+ return status; \
+ \
+ if (!valid_threshold(mcbsp, val)) \
+ return -EDOM; \
+ \
+ mcbsp->prop = val; \
+ return size; \
+} \
+ \
+static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store)
+
+THRESHOLD_PROP_BUILDER(max_tx_thres);
+THRESHOLD_PROP_BUILDER(max_rx_thres);
+
+static const char * const dma_op_modes[] = {
+ "element", "threshold",
+};
+
+static ssize_t dma_op_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ int dma_op_mode, i = 0;
+ ssize_t len = 0;
+ const char * const *s;
+
+ dma_op_mode = mcbsp->dma_op_mode;
+
+ for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
+ if (dma_op_mode == i)
+ len += sprintf(buf + len, "[%s] ", *s);
+ else
+ len += sprintf(buf + len, "%s ", *s);
+ }
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static ssize_t dma_op_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t size)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ int i;
+
+ i = sysfs_match_string(dma_op_modes, buf);
+ if (i < 0)
+ return i;
+
+ spin_lock_irq(&mcbsp->lock);
+ if (!mcbsp->free) {
+ size = -EBUSY;
+ goto unlock;
+ }
+ mcbsp->dma_op_mode = i;
+
+unlock:
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR_RW(dma_op_mode);
+
+static const struct attribute *additional_attrs[] = {
+ &dev_attr_max_tx_thres.attr,
+ &dev_attr_max_rx_thres.attr,
+ &dev_attr_dma_op_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group additional_attr_group = {
+ .attrs = (struct attribute **)additional_attrs,
+};
+
+/*
+ * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
+ * 730 has only 2 McBSP, and both of them are MPU peripherals.
+ */
+static int omap_mcbsp_init(struct platform_device *pdev)
+{
+ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+ struct resource *res;
+ int ret = 0;
+
+ spin_lock_init(&mcbsp->lock);
+ mcbsp->free = true;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mcbsp->io_base))
+ return PTR_ERR(mcbsp->io_base);
+
+ mcbsp->phys_base = res->start;
+ mcbsp->reg_cache_size = resource_size(res);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
+ if (!res)
+ mcbsp->phys_dma_base = mcbsp->phys_base;
+ else
+ mcbsp->phys_dma_base = res->start;
+
+ /*
+ * OMAP1, 2 uses two interrupt lines: TX, RX
+ * OMAP2430, OMAP3 SoC have combined IRQ line as well.
+ * OMAP4 and newer SoC only have the combined IRQ line.
+ * Use the combined IRQ if available since it gives better debugging
+ * possibilities.
+ */
+ mcbsp->irq = platform_get_irq_byname(pdev, "common");
+ if (mcbsp->irq == -ENXIO) {
+ mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
+
+ if (mcbsp->tx_irq == -ENXIO) {
+ mcbsp->irq = platform_get_irq(pdev, 0);
+ mcbsp->tx_irq = 0;
+ } else {
+ mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
+ mcbsp->irq = 0;
+ }
+ }
+
+ if (!pdev->dev.of_node) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
+ if (!res) {
+ dev_err(&pdev->dev, "invalid tx DMA channel\n");
+ return -ENODEV;
+ }
+ mcbsp->dma_req[0] = res->start;
+ mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0];
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
+ if (!res) {
+ dev_err(&pdev->dev, "invalid rx DMA channel\n");
+ return -ENODEV;
+ }
+ mcbsp->dma_req[1] = res->start;
+ mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1];
+ } else {
+ mcbsp->dma_data[0].filter_data = "tx";
+ mcbsp->dma_data[1].filter_data = "rx";
+ }
+
+ mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp,
+ SNDRV_PCM_STREAM_CAPTURE);
+
+ mcbsp->fclk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(mcbsp->fclk)) {
+ ret = PTR_ERR(mcbsp->fclk);
+ dev_err(mcbsp->dev, "unable to get fck: %d\n", ret);
+ return ret;
+ }
+
+ mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
+ if (mcbsp->pdata->buffer_size) {
+ /*
+ * Initially configure the maximum thresholds to a safe value.
+ * The McBSP FIFO usage with these values should not go under
+ * 16 locations.
+ * If the whole FIFO without safety buffer is used, than there
+ * is a possibility that the DMA will be not able to push the
+ * new data on time, causing channel shifts in runtime.
+ */
+ mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
+ mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
+
+ ret = sysfs_create_group(&mcbsp->dev->kobj,
+ &additional_attr_group);
+ if (ret) {
+ dev_err(mcbsp->dev,
+ "Unable to create additional controls\n");
+ goto err_thres;
+ }
+ }
+
+ ret = omap_mcbsp_st_init(pdev);
+ if (ret)
+ goto err_st;
+
+ return 0;
+
+err_st:
+ if (mcbsp->pdata->buffer_size)
+ sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
+err_thres:
+ clk_put(mcbsp->fclk);
+ return ret;
+}
+
+/*
+ * Stream DMA parameters. DMA request line and port address are set runtime
+ * since they are different between OMAP1 and later OMAPs
+ */
+static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream,
+ unsigned int packet_size)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ int words;
+
+ /* No need to proceed further if McBSP does not have FIFO */
+ if (mcbsp->pdata->buffer_size == 0)
+ return;
+
+ /*
+ * Configure McBSP threshold based on either:
+ * packet_size, when the sDMA is in packet mode, or based on the
+ * period size in THRESHOLD mode, otherwise use McBSP threshold = 1
+ * for mono streams.
+ */
+ if (packet_size)
+ words = packet_size;
+ else
+ words = 1;
+
+ /* Configure McBSP internal buffer usage */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ omap_mcbsp_set_tx_threshold(mcbsp, words);
+ else
+ omap_mcbsp_set_rx_threshold(mcbsp, words);
+}
+
+static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_interval *buffer_size = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct omap_mcbsp *mcbsp = rule->private;
+ struct snd_interval frames;
+ int size;
+
+ snd_interval_any(&frames);
+ size = mcbsp->pdata->buffer_size;
+
+ frames.min = size / channels->min;
+ frames.integer = 1;
+ return snd_interval_refine(buffer_size, &frames);
+}
+
+static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ int err = 0;
+
+ if (!cpu_dai->active)
+ err = omap_mcbsp_request(mcbsp);
+
+ /*
+ * OMAP3 McBSP FIFO is word structured.
+ * McBSP2 has 1024 + 256 = 1280 word long buffer,
+ * McBSP1,3,4,5 has 128 word long buffer
+ * This means that the size of the FIFO depends on the sample format.
+ * For example on McBSP3:
+ * 16bit samples: size is 128 * 2 = 256 bytes
+ * 32bit samples: size is 128 * 4 = 512 bytes
+ * It is simpler to place constraint for buffer and period based on
+ * channels.
+ * McBSP3 as example again (16 or 32 bit samples):
+ * 1 channel (mono): size is 128 frames (128 words)
+ * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
+ * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
+ */
+ if (mcbsp->pdata->buffer_size) {
+ /*
+ * Rule for the buffer size. We should not allow
+ * smaller buffer than the FIFO size to avoid underruns.
+ * This applies only for the playback stream.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_pcm_hw_rule_add(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+ omap_mcbsp_hwrule_min_buffersize,
+ mcbsp,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+ /* Make sure, that the period size is always even */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
+ }
+
+ return err;
+}
+
+static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+ int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+
+ if (mcbsp->latency[stream2])
+ pm_qos_update_request(&mcbsp->pm_qos_req,
+ mcbsp->latency[stream2]);
+ else if (mcbsp->latency[stream1])
+ pm_qos_remove_request(&mcbsp->pm_qos_req);
+
+ mcbsp->latency[stream1] = 0;
+
+ if (!cpu_dai->active) {
+ omap_mcbsp_free(mcbsp);
+ mcbsp->configured = 0;
+ }
+}
+
+static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req;
+ int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+ int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+ int latency = mcbsp->latency[stream2];
+
+ /* Prevent omap hardware from hitting off between FIFO fills */
+ if (!latency || mcbsp->latency[stream1] < latency)
+ latency = mcbsp->latency[stream1];
+
+ if (pm_qos_request_active(pm_qos_req))
+ pm_qos_update_request(pm_qos_req, latency);
+ else if (latency)
+ pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
+
+ return 0;
+}
+
+static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mcbsp->active++;
+ omap_mcbsp_start(mcbsp, substream->stream);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ omap_mcbsp_stop(mcbsp, substream->stream);
+ mcbsp->active--;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_sframes_t omap_mcbsp_dai_delay(
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ u16 fifo_use;
+ snd_pcm_sframes_t delay;
+
+ /* No need to proceed further if McBSP does not have FIFO */
+ if (mcbsp->pdata->buffer_size == 0)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ fifo_use = omap_mcbsp_get_tx_delay(mcbsp);
+ else
+ fifo_use = omap_mcbsp_get_rx_delay(mcbsp);
+
+ /*
+ * Divide the used locations with the channel count to get the
+ * FIFO usage in samples (don't care about partial samples in the
+ * buffer).
+ */
+ delay = fifo_use / substream->runtime->channels;
+
+ return delay;
+}
+
+static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ int wlen, channels, wpf;
+ int pkt_size = 0;
+ unsigned int format, div, framesize, master;
+ unsigned int buffer_size = mcbsp->pdata->buffer_size;
+
+ dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+ channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ wlen = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ wlen = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (buffer_size) {
+ int latency;
+
+ if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
+ int period_words, max_thrsh;
+ int divider = 0;
+
+ period_words = params_period_bytes(params) / (wlen / 8);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ max_thrsh = mcbsp->max_tx_thres;
+ else
+ max_thrsh = mcbsp->max_rx_thres;
+ /*
+ * Use sDMA packet mode if McBSP is in threshold mode:
+ * If period words less than the FIFO size the packet
+ * size is set to the number of period words, otherwise
+ * Look for the biggest threshold value which divides
+ * the period size evenly.
+ */
+ divider = period_words / max_thrsh;
+ if (period_words % max_thrsh)
+ divider++;
+ while (period_words % divider &&
+ divider < period_words)
+ divider++;
+ if (divider == period_words)
+ return -EINVAL;
+
+ pkt_size = period_words / divider;
+ } else if (channels > 1) {
+ /* Use packet mode for non mono streams */
+ pkt_size = channels;
+ }
+
+ latency = (buffer_size - pkt_size) / channels;
+ latency = latency * USEC_PER_SEC /
+ (params->rate_num / params->rate_den);
+ mcbsp->latency[substream->stream] = latency;
+
+ omap_mcbsp_set_threshold(substream, pkt_size);
+ }
+
+ dma_data->maxburst = pkt_size;
+
+ if (mcbsp->configured) {
+ /* McBSP already configured by another stream */
+ return 0;
+ }
+
+ regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7));
+ regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7));
+ regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7));
+ regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7));
+ format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ wpf = channels;
+ if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
+ format == SND_SOC_DAIFMT_LEFT_J)) {
+ /* Use dual-phase frames */
+ regs->rcr2 |= RPHASE;
+ regs->xcr2 |= XPHASE;
+ /* Set 1 word per (McBSP) frame for phase1 and phase2 */
+ wpf--;
+ regs->rcr2 |= RFRLEN2(wpf - 1);
+ regs->xcr2 |= XFRLEN2(wpf - 1);
+ }
+
+ regs->rcr1 |= RFRLEN1(wpf - 1);
+ regs->xcr1 |= XFRLEN1(wpf - 1);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ /* Set word lengths */
+ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
+ regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
+ regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
+ regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /* Set word lengths */
+ regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
+ regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
+ regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
+ regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32);
+ break;
+ default:
+ /* Unsupported PCM format */
+ return -EINVAL;
+ }
+
+ /* In McBSP master modes, FRAME (i.e. sample rate) is generated
+ * by _counting_ BCLKs. Calculate frame size in BCLKs */
+ master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK;
+ if (master == SND_SOC_DAIFMT_CBS_CFS) {
+ div = mcbsp->clk_div ? mcbsp->clk_div : 1;
+ framesize = (mcbsp->in_freq / div) / params_rate(params);
+
+ if (framesize < wlen * channels) {
+ printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
+ "channels\n", __func__);
+ return -EINVAL;
+ }
+ } else
+ framesize = wlen * channels;
+
+ /* Set FS period and length in terms of bit clock periods */
+ regs->srgr2 &= ~FPER(0xfff);
+ regs->srgr1 &= ~FWID(0xff);
+ switch (format) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ regs->srgr2 |= FPER(framesize - 1);
+ regs->srgr1 |= FWID((framesize >> 1) - 1);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ regs->srgr2 |= FPER(framesize - 1);
+ regs->srgr1 |= FWID(0);
+ break;
+ }
+
+ omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs);
+ mcbsp->wlen = wlen;
+ mcbsp->configured = 1;
+
+ return 0;
+}
+
+/*
+ * This must be called before _set_clkdiv and _set_sysclk since McBSP register
+ * cache is initialized here
+ */
+static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+ unsigned int fmt)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
+ bool inv_fs = false;
+
+ if (mcbsp->configured)
+ return 0;
+
+ mcbsp->fmt = fmt;
+ memset(regs, 0, sizeof(*regs));
+ /* Generic McBSP register settings */
+ regs->spcr2 |= XINTM(3) | FREE;
+ regs->spcr1 |= RINTM(3);
+ /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */
+ if (!mcbsp->pdata->has_ccr) {
+ regs->rcr2 |= RFIG;
+ regs->xcr2 |= XFIG;
+ }
+
+ /* Configure XCCR/RCCR only for revisions which have ccr registers */
+ if (mcbsp->pdata->has_ccr) {
+ regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
+ regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* 1-bit data delay */
+ regs->rcr2 |= RDATDLY(1);
+ regs->xcr2 |= XDATDLY(1);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ /* 0-bit data delay */
+ regs->rcr2 |= RDATDLY(0);
+ regs->xcr2 |= XDATDLY(0);
+ regs->spcr1 |= RJUST(2);
+ /* Invert FS polarity configuration */
+ inv_fs = true;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /* 1-bit data delay */
+ regs->rcr2 |= RDATDLY(1);
+ regs->xcr2 |= XDATDLY(1);
+ /* Invert FS polarity configuration */
+ inv_fs = true;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ /* 0-bit data delay */
+ regs->rcr2 |= RDATDLY(0);
+ regs->xcr2 |= XDATDLY(0);
+ /* Invert FS polarity configuration */
+ inv_fs = true;
+ break;
+ default:
+ /* Unsupported data format */
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* McBSP master. Set FS and bit clocks as outputs */
+ regs->pcr0 |= FSXM | FSRM |
+ CLKXM | CLKRM;
+ /* Sample rate generator drives the FS */
+ regs->srgr2 |= FSGM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFS:
+ /* McBSP slave. FS clock as output */
+ regs->srgr2 |= FSGM;
+ regs->pcr0 |= FSXM | FSRM;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* McBSP slave */
+ break;
+ default:
+ /* Unsupported master/slave configuration */
+ return -EINVAL;
+ }
+
+ /* Set bit clock (CLKX/CLKR) and FS polarities */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ /*
+ * Normal BCLK + FS.
+ * FS active low. TX data driven on falling edge of bit clock
+ * and RX data sampled on rising edge of bit clock.
+ */
+ regs->pcr0 |= FSXP | FSRP |
+ CLKXP | CLKRP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ regs->pcr0 |= CLKXP | CLKRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regs->pcr0 |= FSXP | FSRP;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (inv_fs == true)
+ regs->pcr0 ^= FSXP | FSRP;
+
+ return 0;
+}
+
+static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
+ int div_id, int div)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
+
+ if (div_id != OMAP_MCBSP_CLKGDV)
+ return -ENODEV;
+
+ mcbsp->clk_div = div;
+ regs->srgr1 &= ~CLKGDV(0xff);
+ regs->srgr1 |= CLKGDV(div - 1);
+
+ return 0;
+}
+
+static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq,
+ int dir)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
+ struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
+ int err = 0;
+
+ if (mcbsp->active) {
+ if (freq == mcbsp->in_freq)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
+ mcbsp->in_freq = freq;
+ regs->srgr2 &= ~CLKSM;
+ regs->pcr0 &= ~SCLKME;
+
+ switch (clk_id) {
+ case OMAP_MCBSP_SYSCLK_CLK:
+ regs->srgr2 |= CLKSM;
+ break;
+ case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
+ if (mcbsp_omap1()) {
+ err = -EINVAL;
+ break;
+ }
+ err = omap2_mcbsp_set_clks_src(mcbsp,
+ MCBSP_CLKS_PRCM_SRC);
+ break;
+ case OMAP_MCBSP_SYSCLK_CLKS_EXT:
+ if (mcbsp_omap1()) {
+ err = 0;
+ break;
+ }
+ err = omap2_mcbsp_set_clks_src(mcbsp,
+ MCBSP_CLKS_PAD_SRC);
+ break;
+
+ case OMAP_MCBSP_SYSCLK_CLKX_EXT:
+ regs->srgr2 |= CLKSM;
+ regs->pcr0 |= SCLKME;
+ /*
+ * If McBSP is master but yet the CLKX/CLKR pin drives the SRG,
+ * disable output on those pins. This enables to inject the
+ * reference clock through CLKX/CLKR. For this to work
+ * set_dai_sysclk() _needs_ to be called after set_dai_fmt().
+ */
+ regs->pcr0 &= ~CLKXM;
+ break;
+ case OMAP_MCBSP_SYSCLK_CLKR_EXT:
+ regs->pcr0 |= SCLKME;
+ /* Disable ouput on CLKR pin in master mode */
+ regs->pcr0 &= ~CLKRM;
+ break;
+ default:
+ err = -ENODEV;
+ }
+
+ return err;
+}
+
+static const struct snd_soc_dai_ops mcbsp_dai_ops = {
+ .startup = omap_mcbsp_dai_startup,
+ .shutdown = omap_mcbsp_dai_shutdown,
+ .prepare = omap_mcbsp_dai_prepare,
+ .trigger = omap_mcbsp_dai_trigger,
+ .delay = omap_mcbsp_dai_delay,
+ .hw_params = omap_mcbsp_dai_hw_params,
+ .set_fmt = omap_mcbsp_dai_set_dai_fmt,
+ .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
+ .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
+};
+
+static int omap_mcbsp_probe(struct snd_soc_dai *dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
+
+ pm_runtime_enable(mcbsp->dev);
+
+ snd_soc_dai_init_dma_data(dai,
+ &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+ return 0;
+}
+
+static int omap_mcbsp_remove(struct snd_soc_dai *dai)
+{
+ struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
+
+ pm_runtime_disable(mcbsp->dev);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver omap_mcbsp_dai = {
+ .probe = omap_mcbsp_probe,
+ .remove = omap_mcbsp_remove,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = OMAP_MCBSP_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = OMAP_MCBSP_RATES,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &mcbsp_dai_ops,
+};
+
+static const struct snd_soc_component_driver omap_mcbsp_component = {
+ .name = "omap-mcbsp",
+};
+
+static struct omap_mcbsp_platform_data omap2420_pdata = {
+ .reg_step = 4,
+ .reg_size = 2,
+};
+
+static struct omap_mcbsp_platform_data omap2430_pdata = {
+ .reg_step = 4,
+ .reg_size = 4,
+ .has_ccr = true,
+};
+
+static struct omap_mcbsp_platform_data omap3_pdata = {
+ .reg_step = 4,
+ .reg_size = 4,
+ .has_ccr = true,
+ .has_wakeup = true,
+};
+
+static struct omap_mcbsp_platform_data omap4_pdata = {
+ .reg_step = 4,
+ .reg_size = 4,
+ .has_ccr = true,
+ .has_wakeup = true,
+};
+
+static const struct of_device_id omap_mcbsp_of_match[] = {
+ {
+ .compatible = "ti,omap2420-mcbsp",
+ .data = &omap2420_pdata,
+ },
+ {
+ .compatible = "ti,omap2430-mcbsp",
+ .data = &omap2430_pdata,
+ },
+ {
+ .compatible = "ti,omap3-mcbsp",
+ .data = &omap3_pdata,
+ },
+ {
+ .compatible = "ti,omap4-mcbsp",
+ .data = &omap4_pdata,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match);
+
+static int asoc_mcbsp_probe(struct platform_device *pdev)
+{
+ struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct omap_mcbsp *mcbsp;
+ const struct of_device_id *match;
+ int ret;
+
+ match = of_match_device(omap_mcbsp_of_match, &pdev->dev);
+ if (match) {
+ struct device_node *node = pdev->dev.of_node;
+ struct omap_mcbsp_platform_data *pdata_quirk = pdata;
+ int buffer_size;
+
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(struct omap_mcbsp_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ memcpy(pdata, match->data, sizeof(*pdata));
+ if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size))
+ pdata->buffer_size = buffer_size;
+ if (pdata_quirk)
+ pdata->force_ick_on = pdata_quirk->force_ick_on;
+ } else if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data.\n");
+ return -EINVAL;
+ }
+ mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL);
+ if (!mcbsp)
+ return -ENOMEM;
+
+ mcbsp->id = pdev->id;
+ mcbsp->pdata = pdata;
+ mcbsp->dev = &pdev->dev;
+ platform_set_drvdata(pdev, mcbsp);
+
+ ret = omap_mcbsp_init(pdev);
+ if (ret)
+ return ret;
+
+ if (mcbsp->pdata->reg_size == 2) {
+ omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &omap_mcbsp_component,
+ &omap_mcbsp_dai, 1);
+ if (ret)
+ return ret;
+
+ return sdma_pcm_platform_register(&pdev->dev, NULL, NULL);
+}
+
+static int asoc_mcbsp_remove(struct platform_device *pdev)
+{
+ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+
+ if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
+ mcbsp->pdata->ops->free(mcbsp->id);
+
+ if (pm_qos_request_active(&mcbsp->pm_qos_req))
+ pm_qos_remove_request(&mcbsp->pm_qos_req);
+
+ if (mcbsp->pdata->buffer_size)
+ sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
+
+ omap_mcbsp_st_cleanup(pdev);
+
+ clk_put(mcbsp->fclk);
+
+ return 0;
+}
+
+static struct platform_driver asoc_mcbsp_driver = {
+ .driver = {
+ .name = "omap-mcbsp",
+ .of_match_table = omap_mcbsp_of_match,
+ },
+
+ .probe = asoc_mcbsp_probe,
+ .remove = asoc_mcbsp_remove,
+};
+
+module_platform_driver(asoc_mcbsp_driver);
+
+MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
+MODULE_DESCRIPTION("OMAP I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-mcbsp");
--- /dev/null
+/*
+ * omap-mcbsp.h
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __OMAP_MCBSP_H__
+#define __OMAP_MCBSP_H__
+
+#include <sound/dmaengine_pcm.h>
+
+/* Source clocks for McBSP sample rate generator */
+enum omap_mcbsp_clksrg_clk {
+ OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */
+ OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */
+ OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */
+ OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */
+ OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */
+};
+
+/* McBSP dividers */
+enum omap_mcbsp_div {
+ OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */
+};
+
+int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id);
+
+#endif /* __OMAP_MCBSP_H__ */
--- /dev/null
+/*
+ * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port
+ *
+ * Copyright (C) 2009 - 2011 Texas Instruments
+ *
+ * Author: Misael Lopez Cruz <misael.lopez@ti.com>
+ * Contact: Jorge Eduardo Candelaria <x0107209@ti.com>
+ * Margarita Olaya <magi.olaya@ti.com>
+ * Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "omap-mcpdm.h"
+#include "sdma-pcm.h"
+
+struct mcpdm_link_config {
+ u32 link_mask; /* channel mask for the direction */
+ u32 threshold; /* FIFO threshold */
+};
+
+struct omap_mcpdm {
+ struct device *dev;
+ unsigned long phys_base;
+ void __iomem *io_base;
+ int irq;
+ struct pm_qos_request pm_qos_req;
+ int latency[2];
+
+ struct mutex mutex;
+
+ /* Playback/Capture configuration */
+ struct mcpdm_link_config config[2];
+
+ /* McPDM dn offsets for rx1, and 2 channels */
+ u32 dn_rx_offset;
+
+ /* McPDM needs to be restarted due to runtime reconfiguration */
+ bool restart;
+
+ /* pm state for suspend/resume handling */
+ int pm_active_count;
+
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+};
+
+/*
+ * Stream DMA parameters
+ */
+
+static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val)
+{
+ writel_relaxed(val, mcpdm->io_base + reg);
+}
+
+static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg)
+{
+ return readl_relaxed(mcpdm->io_base + reg);
+}
+
+#ifdef DEBUG
+static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm)
+{
+ dev_dbg(mcpdm->dev, "***********************\n");
+ dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW));
+ dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS));
+ dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET));
+ dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR));
+ dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN));
+ dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET));
+ dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR));
+ dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN));
+ dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL));
+ dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA));
+ dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA));
+ dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN));
+ dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n",
+ omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP));
+ dev_dbg(mcpdm->dev, "***********************\n");
+}
+#else
+static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {}
+#endif
+
+/*
+ * Enables the transfer through the PDM interface to/from the Phoenix
+ * codec by enabling the corresponding UP or DN channels.
+ */
+static void omap_mcpdm_start(struct omap_mcpdm *mcpdm)
+{
+ u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+ u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask;
+
+ ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
+
+ ctrl |= link_mask;
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
+
+ ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
+}
+
+/*
+ * Disables the transfer through the PDM interface to/from the Phoenix
+ * codec by disabling the corresponding UP or DN channels.
+ */
+static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm)
+{
+ u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+ u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK;
+
+ ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
+
+ ctrl &= ~(link_mask);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
+
+ ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl);
+
+}
+
+/*
+ * Is the physical McPDM interface active.
+ */
+static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
+{
+ return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) &
+ (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK);
+}
+
+/*
+ * Configures McPDM uplink, and downlink for audio.
+ * This function should be called before omap_mcpdm_start.
+ */
+static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
+{
+ u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
+ MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
+ MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
+
+ /* Enable DN RX1/2 offset cancellation feature, if configured */
+ if (mcpdm->dn_rx_offset) {
+ u32 dn_offset = mcpdm->dn_rx_offset;
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
+ dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset);
+ }
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN,
+ mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP,
+ mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold);
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET,
+ MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE);
+}
+
+/*
+ * Cleans McPDM uplink, and downlink configuration.
+ * This function should be called when the stream is closed.
+ */
+static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm)
+{
+ /* Disable irq request generation for downlink */
+ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
+ MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL);
+
+ /* Disable DMA request generation for downlink */
+ omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE);
+
+ /* Disable irq request generation for uplink */
+ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR,
+ MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
+
+ /* Disable DMA request generation for uplink */
+ omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE);
+
+ /* Disable RX1/2 offset cancellation */
+ if (mcpdm->dn_rx_offset)
+ omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0);
+}
+
+static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
+{
+ struct omap_mcpdm *mcpdm = dev_id;
+ int irq_status;
+
+ irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS);
+
+ /* Acknowledge irq event */
+ omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status);
+
+ if (irq_status & MCPDM_DN_IRQ_FULL)
+ dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n");
+
+ if (irq_status & MCPDM_DN_IRQ_EMPTY)
+ dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n");
+
+ if (irq_status & MCPDM_DN_IRQ)
+ dev_dbg(mcpdm->dev, "DN (playback) write request\n");
+
+ if (irq_status & MCPDM_UP_IRQ_FULL)
+ dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n");
+
+ if (irq_status & MCPDM_UP_IRQ_EMPTY)
+ dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n");
+
+ if (irq_status & MCPDM_UP_IRQ)
+ dev_dbg(mcpdm->dev, "UP (capture) write request\n");
+
+ return IRQ_HANDLED;
+}
+
+static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ mutex_lock(&mcpdm->mutex);
+
+ if (!dai->active)
+ omap_mcpdm_open_streams(mcpdm);
+
+ mutex_unlock(&mcpdm->mutex);
+
+ return 0;
+}
+
+static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+ int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+
+ mutex_lock(&mcpdm->mutex);
+
+ if (!dai->active) {
+ if (omap_mcpdm_active(mcpdm)) {
+ omap_mcpdm_stop(mcpdm);
+ omap_mcpdm_close_streams(mcpdm);
+ mcpdm->config[0].link_mask = 0;
+ mcpdm->config[1].link_mask = 0;
+ }
+ }
+
+ if (mcpdm->latency[stream2])
+ pm_qos_update_request(&mcpdm->pm_qos_req,
+ mcpdm->latency[stream2]);
+ else if (mcpdm->latency[stream1])
+ pm_qos_remove_request(&mcpdm->pm_qos_req);
+
+ mcpdm->latency[stream1] = 0;
+
+ mutex_unlock(&mcpdm->mutex);
+}
+
+static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ int stream = substream->stream;
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ u32 threshold;
+ int channels, latency;
+ int link_mask = 0;
+
+ channels = params_channels(params);
+ switch (channels) {
+ case 5:
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ /* up to 3 channels for capture */
+ return -EINVAL;
+ link_mask |= 1 << 4;
+ /* fall through */
+ case 4:
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ /* up to 3 channels for capture */
+ return -EINVAL;
+ link_mask |= 1 << 3;
+ /* fall through */
+ case 3:
+ link_mask |= 1 << 2;
+ /* fall through */
+ case 2:
+ link_mask |= 1 << 1;
+ /* fall through */
+ case 1:
+ link_mask |= 1 << 0;
+ break;
+ default:
+ /* unsupported number of channels */
+ return -EINVAL;
+ }
+
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+
+ threshold = mcpdm->config[stream].threshold;
+ /* Configure McPDM channels, and DMA packet size */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ link_mask <<= 3;
+
+ /* If capture is not running assume a stereo stream to come */
+ if (!mcpdm->config[!stream].link_mask)
+ mcpdm->config[!stream].link_mask = 0x3;
+
+ dma_data->maxburst =
+ (MCPDM_DN_THRES_MAX - threshold) * channels;
+ latency = threshold;
+ } else {
+ /* If playback is not running assume a stereo stream to come */
+ if (!mcpdm->config[!stream].link_mask)
+ mcpdm->config[!stream].link_mask = (0x3 << 3);
+
+ dma_data->maxburst = threshold * channels;
+ latency = (MCPDM_DN_THRES_MAX - threshold);
+ }
+
+ /*
+ * The DMA must act to a DMA request within latency time (usec) to avoid
+ * under/overflow
+ */
+ mcpdm->latency[stream] = latency * USEC_PER_SEC / params_rate(params);
+
+ if (!mcpdm->latency[stream])
+ mcpdm->latency[stream] = 10;
+
+ /* Check if we need to restart McPDM with this stream */
+ if (mcpdm->config[stream].link_mask &&
+ mcpdm->config[stream].link_mask != link_mask)
+ mcpdm->restart = true;
+
+ mcpdm->config[stream].link_mask = link_mask;
+
+ return 0;
+}
+
+static int omap_mcpdm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ struct pm_qos_request *pm_qos_req = &mcpdm->pm_qos_req;
+ int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+ int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
+ int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+ int latency = mcpdm->latency[stream2];
+
+ /* Prevent omap hardware from hitting off between FIFO fills */
+ if (!latency || mcpdm->latency[stream1] < latency)
+ latency = mcpdm->latency[stream1];
+
+ if (pm_qos_request_active(pm_qos_req))
+ pm_qos_update_request(pm_qos_req, latency);
+ else if (latency)
+ pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency);
+
+ if (!omap_mcpdm_active(mcpdm)) {
+ omap_mcpdm_start(mcpdm);
+ omap_mcpdm_reg_dump(mcpdm);
+ } else if (mcpdm->restart) {
+ omap_mcpdm_stop(mcpdm);
+ omap_mcpdm_start(mcpdm);
+ mcpdm->restart = false;
+ omap_mcpdm_reg_dump(mcpdm);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
+ .startup = omap_mcpdm_dai_startup,
+ .shutdown = omap_mcpdm_dai_shutdown,
+ .hw_params = omap_mcpdm_dai_hw_params,
+ .prepare = omap_mcpdm_prepare,
+};
+
+static int omap_mcpdm_probe(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ pm_runtime_enable(mcpdm->dev);
+
+ /* Disable lines while request is ongoing */
+ pm_runtime_get_sync(mcpdm->dev);
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
+
+ ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM",
+ (void *)mcpdm);
+
+ pm_runtime_put_sync(mcpdm->dev);
+
+ if (ret) {
+ dev_err(mcpdm->dev, "Request for IRQ failed\n");
+ pm_runtime_disable(mcpdm->dev);
+ }
+
+ /* Configure McPDM threshold values */
+ mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2;
+ mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold =
+ MCPDM_UP_THRES_MAX - 3;
+
+ snd_soc_dai_init_dma_data(dai,
+ &mcpdm->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &mcpdm->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+ return ret;
+}
+
+static int omap_mcpdm_remove(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ free_irq(mcpdm->irq, (void *)mcpdm);
+ pm_runtime_disable(mcpdm->dev);
+
+ if (pm_qos_request_active(&mcpdm->pm_qos_req))
+ pm_qos_remove_request(&mcpdm->pm_qos_req);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int omap_mcpdm_suspend(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ if (dai->active) {
+ omap_mcpdm_stop(mcpdm);
+ omap_mcpdm_close_streams(mcpdm);
+ }
+
+ mcpdm->pm_active_count = 0;
+ while (pm_runtime_active(mcpdm->dev)) {
+ pm_runtime_put_sync(mcpdm->dev);
+ mcpdm->pm_active_count++;
+ }
+
+ return 0;
+}
+
+static int omap_mcpdm_resume(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ if (mcpdm->pm_active_count) {
+ while (mcpdm->pm_active_count--)
+ pm_runtime_get_sync(mcpdm->dev);
+
+ if (dai->active) {
+ omap_mcpdm_open_streams(mcpdm);
+ omap_mcpdm_start(mcpdm);
+ }
+ }
+
+
+ return 0;
+}
+#else
+#define omap_mcpdm_suspend NULL
+#define omap_mcpdm_resume NULL
+#endif
+
+#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
+
+static struct snd_soc_dai_driver omap_mcpdm_dai = {
+ .probe = omap_mcpdm_probe,
+ .remove = omap_mcpdm_remove,
+ .suspend = omap_mcpdm_suspend,
+ .resume = omap_mcpdm_resume,
+ .probe_order = SND_SOC_COMP_ORDER_LATE,
+ .remove_order = SND_SOC_COMP_ORDER_EARLY,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 5,
+ .rates = OMAP_MCPDM_RATES,
+ .formats = OMAP_MCPDM_FORMATS,
+ .sig_bits = 24,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 3,
+ .rates = OMAP_MCPDM_RATES,
+ .formats = OMAP_MCPDM_FORMATS,
+ .sig_bits = 24,
+ },
+ .ops = &omap_mcpdm_dai_ops,
+};
+
+static const struct snd_soc_component_driver omap_mcpdm_component = {
+ .name = "omap-mcpdm",
+};
+
+void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
+ u8 rx1, u8 rx2)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+ mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2);
+}
+EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets);
+
+static int asoc_mcpdm_probe(struct platform_device *pdev)
+{
+ struct omap_mcpdm *mcpdm;
+ struct resource *res;
+ int ret;
+
+ mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL);
+ if (!mcpdm)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, mcpdm);
+
+ mutex_init(&mcpdm->mutex);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
+ if (res == NULL)
+ return -ENOMEM;
+
+ mcpdm->dma_data[0].addr = res->start + MCPDM_REG_DN_DATA;
+ mcpdm->dma_data[1].addr = res->start + MCPDM_REG_UP_DATA;
+
+ mcpdm->dma_data[0].filter_data = "dn_link";
+ mcpdm->dma_data[1].filter_data = "up_link";
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
+ mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mcpdm->io_base))
+ return PTR_ERR(mcpdm->io_base);
+
+ mcpdm->irq = platform_get_irq(pdev, 0);
+ if (mcpdm->irq < 0)
+ return mcpdm->irq;
+
+ mcpdm->dev = &pdev->dev;
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &omap_mcpdm_component,
+ &omap_mcpdm_dai, 1);
+ if (ret)
+ return ret;
+
+ return sdma_pcm_platform_register(&pdev->dev, "dn_link", "up_link");
+}
+
+static const struct of_device_id omap_mcpdm_of_match[] = {
+ { .compatible = "ti,omap4-mcpdm", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match);
+
+static struct platform_driver asoc_mcpdm_driver = {
+ .driver = {
+ .name = "omap-mcpdm",
+ .of_match_table = omap_mcpdm_of_match,
+ },
+
+ .probe = asoc_mcpdm_probe,
+};
+
+module_platform_driver(asoc_mcpdm_driver);
+
+MODULE_ALIAS("platform:omap-mcpdm");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
+MODULE_DESCRIPTION("OMAP PDM SoC Interface");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * omap-mcpdm.h
+ *
+ * Copyright (C) 2009 - 2011 Texas Instruments
+ *
+ * Contact: Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * 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
+ *
+ */
+
+#ifndef __OMAP_MCPDM_H__
+#define __OMAP_MCPDM_H__
+
+#define MCPDM_REG_REVISION 0x00
+#define MCPDM_REG_SYSCONFIG 0x10
+#define MCPDM_REG_IRQSTATUS_RAW 0x24
+#define MCPDM_REG_IRQSTATUS 0x28
+#define MCPDM_REG_IRQENABLE_SET 0x2C
+#define MCPDM_REG_IRQENABLE_CLR 0x30
+#define MCPDM_REG_IRQWAKE_EN 0x34
+#define MCPDM_REG_DMAENABLE_SET 0x38
+#define MCPDM_REG_DMAENABLE_CLR 0x3C
+#define MCPDM_REG_DMAWAKEEN 0x40
+#define MCPDM_REG_CTRL 0x44
+#define MCPDM_REG_DN_DATA 0x48
+#define MCPDM_REG_UP_DATA 0x4C
+#define MCPDM_REG_FIFO_CTRL_DN 0x50
+#define MCPDM_REG_FIFO_CTRL_UP 0x54
+#define MCPDM_REG_DN_OFFSET 0x58
+
+/*
+ * MCPDM_IRQ bit fields
+ * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
+ */
+
+#define MCPDM_DN_IRQ (1 << 0)
+#define MCPDM_DN_IRQ_EMPTY (1 << 1)
+#define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2)
+#define MCPDM_DN_IRQ_FULL (1 << 3)
+
+#define MCPDM_UP_IRQ (1 << 8)
+#define MCPDM_UP_IRQ_EMPTY (1 << 9)
+#define MCPDM_UP_IRQ_ALMST_FULL (1 << 10)
+#define MCPDM_UP_IRQ_FULL (1 << 11)
+
+#define MCPDM_DOWNLINK_IRQ_MASK 0x00F
+#define MCPDM_UPLINK_IRQ_MASK 0xF00
+
+/*
+ * MCPDM_DMAENABLE bit fields
+ */
+
+#define MCPDM_DMA_DN_ENABLE (1 << 0)
+#define MCPDM_DMA_UP_ENABLE (1 << 1)
+
+/*
+ * MCPDM_CTRL bit fields
+ */
+
+#define MCPDM_PDM_UPLINK_EN(x) (1 << (x - 1)) /* ch1 is at bit 0 */
+#define MCPDM_PDM_DOWNLINK_EN(x) (1 << (x + 2)) /* ch1 is at bit 3 */
+#define MCPDM_PDMOUTFORMAT (1 << 8)
+#define MCPDM_CMD_INT (1 << 9)
+#define MCPDM_STATUS_INT (1 << 10)
+#define MCPDM_SW_UP_RST (1 << 11)
+#define MCPDM_SW_DN_RST (1 << 12)
+#define MCPDM_WD_EN (1 << 14)
+#define MCPDM_PDM_UP_MASK 0x7
+#define MCPDM_PDM_DN_MASK (0x1f << 3)
+
+
+#define MCPDM_PDMOUTFORMAT_LJUST (0 << 8)
+#define MCPDM_PDMOUTFORMAT_RJUST (1 << 8)
+
+/*
+ * MCPDM_FIFO_CTRL bit fields
+ */
+
+#define MCPDM_UP_THRES_MAX 0xF
+#define MCPDM_DN_THRES_MAX 0xF
+
+/*
+ * MCPDM_DN_OFFSET bit fields
+ */
+
+#define MCPDM_DN_OFST_RX1_EN (1 << 0)
+#define MCPDM_DNOFST_RX1(x) ((x & 0x1f) << 1)
+#define MCPDM_DN_OFST_RX2_EN (1 << 8)
+#define MCPDM_DNOFST_RX2(x) ((x & 0x1f) << 9)
+
+void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd,
+ u8 rx1, u8 rx2);
+
+#endif /* End of __OMAP_MCPDM_H__ */
--- /dev/null
+/*
+ * omap-twl4030.c -- SoC audio for TI SoC based boards with twl4030 codec
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * All rights reserved.
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ *
+ * This driver replaces the following machine drivers:
+ * omap3beagle (Author: Steve Sakoman <steve@sakoman.com>)
+ * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
+ * overo (Author: Steve Sakoman <steve@sakoman.com>)
+ * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
+ * zoom2 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
+ * sdp3430 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
+ *
+ * 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
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/platform_data/omap-twl4030.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "omap-mcbsp.h"
+
+struct omap_twl4030 {
+ int jack_detect; /* board can detect jack events */
+ struct snd_soc_jack hs_jack;
+};
+
+static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int fmt;
+
+ switch (params_channels(params)) {
+ case 2: /* Stereo I2S mode */
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ case 4: /* Four channel TDM mode */
+ fmt = SND_SOC_DAIFMT_DSP_A |
+ SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_runtime_set_dai_fmt(rtd, fmt);
+}
+
+static const struct snd_soc_ops omap_twl4030_ops = {
+ .hw_params = omap_twl4030_hw_params,
+};
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
+ SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
+ SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_SPK("Carkit Spk", NULL),
+
+ SND_SOC_DAPM_MIC("Main Mic", NULL),
+ SND_SOC_DAPM_MIC("Sub Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Carkit Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ /* Headset Stereophone: HSOL, HSOR */
+ {"Headset Stereophone", NULL, "HSOL"},
+ {"Headset Stereophone", NULL, "HSOR"},
+ /* External Speakers: HFL, HFR */
+ {"Handsfree Spk", NULL, "HFL"},
+ {"Handsfree Spk", NULL, "HFR"},
+ /* External Speakers: PredrivL, PredrivR */
+ {"Ext Spk", NULL, "PREDRIVEL"},
+ {"Ext Spk", NULL, "PREDRIVER"},
+ /* Carkit speakers: CARKITL, CARKITR */
+ {"Carkit Spk", NULL, "CARKITL"},
+ {"Carkit Spk", NULL, "CARKITR"},
+ /* Earpiece */
+ {"Earpiece Spk", NULL, "EARPIECE"},
+
+ /* External Mics: MAINMIC, SUBMIC with bias */
+ {"MAINMIC", NULL, "Main Mic"},
+ {"Main Mic", NULL, "Mic Bias 1"},
+ {"SUBMIC", NULL, "Sub Mic"},
+ {"Sub Mic", NULL, "Mic Bias 2"},
+ /* Headset Mic: HSMIC with bias */
+ {"HSMIC", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "Headset Mic Bias"},
+ /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
+ {"DIGIMIC0", NULL, "Digital0 Mic"},
+ {"Digital0 Mic", NULL, "Mic Bias 1"},
+ {"DIGIMIC1", NULL, "Digital1 Mic"},
+ {"Digital1 Mic", NULL, "Mic Bias 2"},
+ /* Carkit In: CARKITMIC */
+ {"CARKITMIC", NULL, "Carkit Mic"},
+ /* Aux In: AUXL, AUXR */
+ {"AUXL", NULL, "Line In"},
+ {"AUXR", NULL, "Line In"},
+};
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headset Stereophone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+/* Headset jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+ {
+ .name = "hsdet-gpio",
+ .report = SND_JACK_HEADSET,
+ .debounce_time = 200,
+ },
+};
+
+static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
+ int connected, char *pin)
+{
+ if (!connected)
+ snd_soc_dapm_disable_pin(dapm, pin);
+}
+
+static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = &card->dapm;
+ struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
+ struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
+ int ret = 0;
+
+ /* Headset jack detection only if it is supported */
+ if (priv->jack_detect > 0) {
+ hs_jack_gpios[0].gpio = priv->jack_detect;
+
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET, &priv->hs_jack,
+ hs_jack_pins,
+ ARRAY_SIZE(hs_jack_pins));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_gpios(&priv->hs_jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * NULL pdata means we booted with DT. In this case the routing is
+ * provided and the card is fully routed, no need to mark pins.
+ */
+ if (!pdata || !pdata->custom_routing)
+ return ret;
+
+ /* Disable not connected paths if not used */
+ twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
+ twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
+ twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
+ twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
+ twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
+
+ twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
+ twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
+ twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
+ twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
+ twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
+ twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
+ twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
+
+ return ret;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
+ {
+ .name = "TWL4030 HiFi",
+ .stream_name = "TWL4030 HiFi",
+ .cpu_dai_name = "omap-mcbsp.2",
+ .codec_dai_name = "twl4030-hifi",
+ .platform_name = "omap-mcbsp.2",
+ .codec_name = "twl4030-codec",
+ .init = omap_twl4030_init,
+ .ops = &omap_twl4030_ops,
+ },
+ {
+ .name = "TWL4030 Voice",
+ .stream_name = "TWL4030 Voice",
+ .cpu_dai_name = "omap-mcbsp.3",
+ .codec_dai_name = "twl4030-voice",
+ .platform_name = "omap-mcbsp.3",
+ .codec_name = "twl4030-codec",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ },
+};
+
+/* Audio machine driver */
+static struct snd_soc_card omap_twl4030_card = {
+ .owner = THIS_MODULE,
+ .dai_link = omap_twl4030_dai_links,
+ .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
+
+ .dapm_widgets = dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static int omap_twl4030_probe(struct platform_device *pdev)
+{
+ struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
+ struct device_node *node = pdev->dev.of_node;
+ struct snd_soc_card *card = &omap_twl4030_card;
+ struct omap_twl4030 *priv;
+ int ret = 0;
+
+ card->dev = &pdev->dev;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ if (node) {
+ struct device_node *dai_node;
+ struct property *prop;
+
+ if (snd_soc_of_parse_card_name(card, "ti,model")) {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+
+ dai_node = of_parse_phandle(node, "ti,mcbsp", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McBSP node is not provided\n");
+ return -EINVAL;
+ }
+ omap_twl4030_dai_links[0].cpu_dai_name = NULL;
+ omap_twl4030_dai_links[0].cpu_of_node = dai_node;
+
+ omap_twl4030_dai_links[0].platform_name = NULL;
+ omap_twl4030_dai_links[0].platform_of_node = dai_node;
+
+ dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
+ if (!dai_node) {
+ card->num_links = 1;
+ } else {
+ omap_twl4030_dai_links[1].cpu_dai_name = NULL;
+ omap_twl4030_dai_links[1].cpu_of_node = dai_node;
+
+ omap_twl4030_dai_links[1].platform_name = NULL;
+ omap_twl4030_dai_links[1].platform_of_node = dai_node;
+ }
+
+ priv->jack_detect = of_get_named_gpio(node,
+ "ti,jack-det-gpio", 0);
+
+ /* Optional: audio routing can be provided */
+ prop = of_find_property(node, "ti,audio-routing", NULL);
+ if (prop) {
+ ret = snd_soc_of_parse_audio_routing(card,
+ "ti,audio-routing");
+ if (ret)
+ return ret;
+
+ card->fully_routed = 1;
+ }
+ } else if (pdata) {
+ if (pdata->card_name) {
+ card->name = pdata->card_name;
+ } else {
+ dev_err(&pdev->dev, "Card name is not provided\n");
+ return -ENODEV;
+ }
+
+ if (!pdata->voice_connected)
+ card->num_links = 1;
+
+ priv->jack_detect = pdata->jack_detect;
+ } else {
+ dev_err(&pdev->dev, "Missing pdata\n");
+ return -ENODEV;
+ }
+
+ snd_soc_card_set_drvdata(card, priv);
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id omap_twl4030_of_match[] = {
+ {.compatible = "ti,omap-twl4030", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, omap_twl4030_of_match);
+
+static struct platform_driver omap_twl4030_driver = {
+ .driver = {
+ .name = "omap-twl4030",
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = omap_twl4030_of_match,
+ },
+ .probe = omap_twl4030_probe,
+};
+
+module_platform_driver(omap_twl4030_driver);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-twl4030");
--- /dev/null
+/*
+ * omap3pandora.c -- SoC audio for Pandora Handheld Console
+ *
+ * Author: Gražvydas Ignotas <notasas@gmail.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/module.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#include "omap-mcbsp.h"
+
+#define OMAP3_PANDORA_DAC_POWER_GPIO 118
+#define OMAP3_PANDORA_AMP_POWER_GPIO 14
+
+#define PREFIX "ASoC omap3pandora: "
+
+static struct regulator *omap3pandora_dac_reg;
+
+static int omap3pandora_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int ret;
+
+ /* Set the codec system clock for DAC and ADC */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set codec system clock\n");
+ return ret;
+ }
+
+ /* Set McBSP clock to external */
+ ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
+ 256 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set cpu system clock\n");
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
+ if (ret < 0) {
+ pr_err(PREFIX "can't set SRG clock divider\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret;
+
+ /*
+ * The PCM1773 DAC datasheet requires 1ms delay between switching
+ * VCC power on/off and /PD pin high/low
+ */
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = regulator_enable(omap3pandora_dac_reg);
+ if (ret) {
+ dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret);
+ return ret;
+ }
+ mdelay(1);
+ gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
+ } else {
+ gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+ mdelay(1);
+ regulator_disable(omap3pandora_dac_reg);
+ }
+
+ return 0;
+}
+
+static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
+ else
+ gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+
+ return 0;
+}
+
+/*
+ * Audio paths on Pandora board:
+ *
+ * |O| ---> PCM DAC +-> AMP -> Headphone Jack
+ * |M| A +--------> Line Out
+ * |A| <~~clk~~+
+ * |P| <--- TWL4030 <--------- Line In and MICs
+ */
+static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
+ 0, 0, omap3pandora_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
+ 0, 0, NULL, 0, omap3pandora_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+
+ SND_SOC_DAPM_MIC("Mic (internal)", NULL),
+ SND_SOC_DAPM_MIC("Mic (external)", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route omap3pandora_map[] = {
+ {"PCM DAC", NULL, "APLL Enable"},
+ {"Headphone Amplifier", NULL, "PCM DAC"},
+ {"Line Out", NULL, "PCM DAC"},
+ {"Headphone Jack", NULL, "Headphone Amplifier"},
+
+ {"AUXL", NULL, "Line In"},
+ {"AUXR", NULL, "Line In"},
+
+ {"MAINMIC", NULL, "Mic (internal)"},
+ {"Mic (internal)", NULL, "Mic Bias 1"},
+
+ {"SUBMIC", NULL, "Mic (external)"},
+ {"Mic (external)", NULL, "Mic Bias 2"},
+};
+
+static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+ /* All TWL4030 output pins are floating */
+ snd_soc_dapm_nc_pin(dapm, "EARPIECE");
+ snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
+ snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
+ snd_soc_dapm_nc_pin(dapm, "HSOL");
+ snd_soc_dapm_nc_pin(dapm, "HSOR");
+ snd_soc_dapm_nc_pin(dapm, "CARKITL");
+ snd_soc_dapm_nc_pin(dapm, "CARKITR");
+ snd_soc_dapm_nc_pin(dapm, "HFL");
+ snd_soc_dapm_nc_pin(dapm, "HFR");
+ snd_soc_dapm_nc_pin(dapm, "VIBRA");
+
+ return 0;
+}
+
+static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dapm_context *dapm = &rtd->card->dapm;
+
+ /* Not comnnected */
+ snd_soc_dapm_nc_pin(dapm, "HSMIC");
+ snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
+ snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
+ snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
+
+ return 0;
+}
+
+static const struct snd_soc_ops omap3pandora_ops = {
+ .hw_params = omap3pandora_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3pandora_dai[] = {
+ {
+ .name = "PCM1773",
+ .stream_name = "HiFi Out",
+ .cpu_dai_name = "omap-mcbsp.2",
+ .codec_dai_name = "twl4030-hifi",
+ .platform_name = "omap-mcbsp.2",
+ .codec_name = "twl4030-codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &omap3pandora_ops,
+ .init = omap3pandora_out_init,
+ }, {
+ .name = "TWL4030",
+ .stream_name = "Line/Mic In",
+ .cpu_dai_name = "omap-mcbsp.4",
+ .codec_dai_name = "twl4030-hifi",
+ .platform_name = "omap-mcbsp.4",
+ .codec_name = "twl4030-codec",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ops = &omap3pandora_ops,
+ .init = omap3pandora_in_init,
+ }
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_omap3pandora = {
+ .name = "omap3pandora",
+ .owner = THIS_MODULE,
+ .dai_link = omap3pandora_dai,
+ .num_links = ARRAY_SIZE(omap3pandora_dai),
+
+ .dapm_widgets = omap3pandora_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
+ .dapm_routes = omap3pandora_map,
+ .num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
+};
+
+static struct platform_device *omap3pandora_snd_device;
+
+static int __init omap3pandora_soc_init(void)
+{
+ int ret;
+
+ if (!machine_is_omap3_pandora())
+ return -ENODEV;
+
+ pr_info("OMAP3 Pandora SoC init\n");
+
+ ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
+ if (ret) {
+ pr_err(PREFIX "Failed to get DAC power GPIO\n");
+ return ret;
+ }
+
+ ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+ if (ret) {
+ pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
+ goto fail0;
+ }
+
+ ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
+ if (ret) {
+ pr_err(PREFIX "Failed to get amp power GPIO\n");
+ goto fail0;
+ }
+
+ ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
+ if (ret) {
+ pr_err(PREFIX "Failed to set amp power GPIO direction\n");
+ goto fail1;
+ }
+
+ omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
+ if (omap3pandora_snd_device == NULL) {
+ pr_err(PREFIX "Platform device allocation failed\n");
+ ret = -ENOMEM;
+ goto fail1;
+ }
+
+ platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
+
+ ret = platform_device_add(omap3pandora_snd_device);
+ if (ret) {
+ pr_err(PREFIX "Unable to add platform device\n");
+ goto fail2;
+ }
+
+ omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
+ if (IS_ERR(omap3pandora_dac_reg)) {
+ pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
+ dev_name(&omap3pandora_snd_device->dev),
+ PTR_ERR(omap3pandora_dac_reg));
+ ret = PTR_ERR(omap3pandora_dac_reg);
+ goto fail3;
+ }
+
+ return 0;
+
+fail3:
+ platform_device_del(omap3pandora_snd_device);
+fail2:
+ platform_device_put(omap3pandora_snd_device);
+fail1:
+ gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
+fail0:
+ gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
+ return ret;
+}
+module_init(omap3pandora_soc_init);
+
+static void __exit omap3pandora_soc_exit(void)
+{
+ regulator_put(omap3pandora_dac_reg);
+ platform_device_unregister(omap3pandora_snd_device);
+ gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
+ gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
+}
+module_exit(omap3pandora_soc_exit);
+
+MODULE_AUTHOR("Grazvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * osk5912.c -- SoC audio for OSK 5912
+ *
+ * Copyright (C) 2008 Mistral Solutions
+ *
+ * Contact: Arun KS <arunks@mistralsolutions.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK 12000000
+
+static struct clk *tlv320aic23_mclk;
+
+static int osk_startup(struct snd_pcm_substream *substream)
+{
+ return clk_enable(tlv320aic23_mclk);
+}
+
+static void osk_shutdown(struct snd_pcm_substream *substream)
+{
+ clk_disable(tlv320aic23_mclk);
+}
+
+static int osk_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int err;
+
+ /* Set the codec system clock for DAC and ADC */
+ err =
+ snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
+
+ if (err < 0) {
+ printk(KERN_ERR "can't set codec system clock\n");
+ return err;
+ }
+
+ return err;
+}
+
+static const struct snd_soc_ops osk_ops = {
+ .startup = osk_startup,
+ .hw_params = osk_hw_params,
+ .shutdown = osk_shutdown,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "LHPOUT"},
+ {"Headphone Jack", NULL, "RHPOUT"},
+
+ {"LLINEIN", NULL, "Line In"},
+ {"RLINEIN", NULL, "Line In"},
+
+ {"MICIN", NULL, "Mic Jack"},
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link osk_dai = {
+ .name = "TLV320AIC23",
+ .stream_name = "AIC23",
+ .cpu_dai_name = "omap-mcbsp.1",
+ .codec_dai_name = "tlv320aic23-hifi",
+ .platform_name = "omap-mcbsp.1",
+ .codec_name = "tlv320aic23-codec",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .ops = &osk_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_card_osk = {
+ .name = "OSK5912",
+ .owner = THIS_MODULE,
+ .dai_link = &osk_dai,
+ .num_links = 1,
+
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static struct platform_device *osk_snd_device;
+
+static int __init osk_soc_init(void)
+{
+ int err;
+ u32 curRate;
+ struct device *dev;
+
+ if (!(machine_is_omap_osk()))
+ return -ENODEV;
+
+ osk_snd_device = platform_device_alloc("soc-audio", -1);
+ if (!osk_snd_device)
+ return -ENOMEM;
+
+ platform_set_drvdata(osk_snd_device, &snd_soc_card_osk);
+ err = platform_device_add(osk_snd_device);
+ if (err)
+ goto err1;
+
+ dev = &osk_snd_device->dev;
+
+ tlv320aic23_mclk = clk_get(dev, "mclk");
+ if (IS_ERR(tlv320aic23_mclk)) {
+ printk(KERN_ERR "Could not get mclk clock\n");
+ err = PTR_ERR(tlv320aic23_mclk);
+ goto err2;
+ }
+
+ /*
+ * Configure 12 MHz output on MCLK.
+ */
+ curRate = (uint) clk_get_rate(tlv320aic23_mclk);
+ if (curRate != CODEC_CLOCK) {
+ if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) {
+ printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n");
+ err = -ECANCELED;
+ goto err3;
+ }
+ }
+
+ printk(KERN_INFO "MCLK = %d [%d]\n",
+ (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
+
+ return 0;
+
+err3:
+ clk_put(tlv320aic23_mclk);
+err2:
+ platform_device_del(osk_snd_device);
+err1:
+ platform_device_put(osk_snd_device);
+
+ return err;
+
+}
+
+static void __exit osk_soc_exit(void)
+{
+ clk_put(tlv320aic23_mclk);
+ platform_device_unregister(osk_snd_device);
+}
+
+module_init(osk_soc_init);
+module_exit(osk_soc_exit);
+
+MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>");
+MODULE_DESCRIPTION("ALSA SoC OSK 5912");
+MODULE_LICENSE("GPL");
--- /dev/null
+/*
+ * rx51.c -- SoC audio for Nokia RX-51
+ *
+ * Copyright (C) 2008 - 2009 Nokia Corporation
+ *
+ * Contact: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ * Eduardo Valentin <eduardo.valentin@nokia.com>
+ * Jarkko Nikula <jarkko.nikula@bitmer.com>
+ *
+ * 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
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/platform_data/asoc-ti-mcbsp.h>
+
+#include <asm/mach-types.h>
+
+#include "omap-mcbsp.h"
+
+enum {
+ RX51_JACK_DISABLED,
+ RX51_JACK_TVOUT, /* tv-out with stereo output */
+ RX51_JACK_HP, /* headphone: stereo output, no mic */
+ RX51_JACK_HS, /* headset: stereo output with mic */
+};
+
+struct rx51_audio_pdata {
+ struct gpio_desc *tvout_selection_gpio;
+ struct gpio_desc *jack_detection_gpio;
+ struct gpio_desc *eci_sw_gpio;
+ struct gpio_desc *speaker_amp_gpio;
+};
+
+static int rx51_spk_func;
+static int rx51_dmic_func;
+static int rx51_jack_func;
+
+static void rx51_ext_control(struct snd_soc_dapm_context *dapm)
+{
+ struct snd_soc_card *card = dapm->card;
+ struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+ int hp = 0, hs = 0, tvout = 0;
+
+ switch (rx51_jack_func) {
+ case RX51_JACK_TVOUT:
+ tvout = 1;
+ hp = 1;
+ break;
+ case RX51_JACK_HS:
+ hs = 1;
+ case RX51_JACK_HP:
+ hp = 1;
+ break;
+ }
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (rx51_spk_func)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk");
+ if (rx51_dmic_func)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "DMic");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "DMic");
+ if (hp)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack");
+ if (hs)
+ snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic");
+ else
+ snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic");
+
+ gpiod_set_value(pdata->tvout_selection_gpio, tvout);
+
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static int rx51_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ rx51_ext_control(&card->dapm);
+
+ return 0;
+}
+
+static int rx51_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ /* Set the codec system clock for DAC and ADC */
+ return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000,
+ SND_SOC_CLOCK_IN);
+}
+
+static const struct snd_soc_ops rx51_ops = {
+ .startup = rx51_startup,
+ .hw_params = rx51_hw_params,
+};
+
+static int rx51_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = rx51_spk_func;
+
+ return 0;
+}
+
+static int rx51_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (rx51_spk_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ rx51_spk_func = ucontrol->value.enumerated.item[0];
+ rx51_ext_control(&card->dapm);
+
+ return 1;
+}
+
+static int rx51_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+
+ gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio,
+ !!SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int rx51_get_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = rx51_dmic_func;
+
+ return 0;
+}
+
+static int rx51_set_input(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (rx51_dmic_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ rx51_dmic_func = ucontrol->value.enumerated.item[0];
+ rx51_ext_control(&card->dapm);
+
+ return 1;
+}
+
+static int rx51_get_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.enumerated.item[0] = rx51_jack_func;
+
+ return 0;
+}
+
+static int rx51_set_jack(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+
+ if (rx51_jack_func == ucontrol->value.enumerated.item[0])
+ return 0;
+
+ rx51_jack_func = ucontrol->value.enumerated.item[0];
+ rx51_ext_control(&card->dapm);
+
+ return 1;
+}
+
+static struct snd_soc_jack rx51_av_jack;
+
+static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
+ {
+ .name = "avdet-gpio",
+ .report = SND_JACK_HEADSET,
+ .invert = 1,
+ .debounce_time = 200,
+ },
+};
+
+static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
+ SND_SOC_DAPM_MIC("DMic", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("HS Mic", NULL),
+ SND_SOC_DAPM_LINE("FM Transmitter", NULL),
+ SND_SOC_DAPM_SPK("Earphone", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Ext Spk", NULL, "HPLOUT"},
+ {"Ext Spk", NULL, "HPROUT"},
+ {"Ext Spk", NULL, "HPLCOM"},
+ {"Ext Spk", NULL, "HPRCOM"},
+ {"FM Transmitter", NULL, "LLOUT"},
+ {"FM Transmitter", NULL, "RLOUT"},
+
+ {"Headphone Jack", NULL, "TPA6130A2 HPLEFT"},
+ {"Headphone Jack", NULL, "TPA6130A2 HPRIGHT"},
+ {"TPA6130A2 LEFTIN", NULL, "LLOUT"},
+ {"TPA6130A2 RIGHTIN", NULL, "RLOUT"},
+
+ {"DMic Rate 64", NULL, "DMic"},
+ {"DMic", NULL, "Mic Bias"},
+
+ {"b LINE2R", NULL, "MONO_LOUT"},
+ {"Earphone", NULL, "b HPLOUT"},
+
+ {"LINE1L", NULL, "HS Mic"},
+ {"HS Mic", NULL, "b Mic Bias"},
+};
+
+static const char * const spk_function[] = {"Off", "On"};
+static const char * const input_function[] = {"ADC", "Digital Mic"};
+static const char * const jack_function[] = {
+ "Off", "TV-OUT", "Headphone", "Headset"
+};
+
+static const struct soc_enum rx51_enum[] = {
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function),
+};
+
+static const struct snd_kcontrol_new aic34_rx51_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", rx51_enum[0],
+ rx51_get_spk, rx51_set_spk),
+ SOC_ENUM_EXT("Input Select", rx51_enum[1],
+ rx51_get_input, rx51_set_input),
+ SOC_ENUM_EXT("Jack Function", rx51_enum[2],
+ rx51_get_jack, rx51_set_jack),
+ SOC_DAPM_PIN_SWITCH("FM Transmitter"),
+ SOC_DAPM_PIN_SWITCH("Earphone"),
+};
+
+static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
+ int err;
+
+ snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
+
+ err = omap_mcbsp_st_add_controls(rtd, 2);
+ if (err < 0) {
+ dev_err(card->dev, "Failed to add MCBSP controls\n");
+ return err;
+ }
+
+ /* AV jack detection */
+ err = snd_soc_card_jack_new(rtd->card, "AV Jack",
+ SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
+ &rx51_av_jack, NULL, 0);
+ if (err) {
+ dev_err(card->dev, "Failed to add AV Jack\n");
+ return err;
+ }
+
+ /* prepare gpio for snd_soc_jack_add_gpios */
+ rx51_av_jack_gpios[0].gpio = desc_to_gpio(pdata->jack_detection_gpio);
+ devm_gpiod_put(card->dev, pdata->jack_detection_gpio);
+
+ err = snd_soc_jack_add_gpios(&rx51_av_jack,
+ ARRAY_SIZE(rx51_av_jack_gpios),
+ rx51_av_jack_gpios);
+ if (err) {
+ dev_err(card->dev, "Failed to add GPIOs\n");
+ return err;
+ }
+
+ return err;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link rx51_dai[] = {
+ {
+ .name = "TLV320AIC34",
+ .stream_name = "AIC34",
+ .cpu_dai_name = "omap-mcbsp.2",
+ .codec_dai_name = "tlv320aic3x-hifi",
+ .platform_name = "omap-mcbsp.2",
+ .codec_name = "tlv320aic3x-codec.2-0018",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+ SND_SOC_DAIFMT_CBM_CFM,
+ .init = rx51_aic34_init,
+ .ops = &rx51_ops,
+ },
+};
+
+static struct snd_soc_aux_dev rx51_aux_dev[] = {
+ {
+ .name = "TLV320AIC34b",
+ .codec_name = "tlv320aic3x-codec.2-0019",
+ },
+ {
+ .name = "TPA61320A2",
+ .codec_name = "tpa6130a2.2-0060",
+ },
+};
+
+static struct snd_soc_codec_conf rx51_codec_conf[] = {
+ {
+ .dev_name = "tlv320aic3x-codec.2-0019",
+ .name_prefix = "b",
+ },
+ {
+ .dev_name = "tpa6130a2.2-0060",
+ .name_prefix = "TPA6130A2",
+ },
+};
+
+/* Audio card */
+static struct snd_soc_card rx51_sound_card = {
+ .name = "RX-51",
+ .owner = THIS_MODULE,
+ .dai_link = rx51_dai,
+ .num_links = ARRAY_SIZE(rx51_dai),
+ .aux_dev = rx51_aux_dev,
+ .num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
+ .codec_conf = rx51_codec_conf,
+ .num_configs = ARRAY_SIZE(rx51_codec_conf),
+ .fully_routed = true,
+
+ .controls = aic34_rx51_controls,
+ .num_controls = ARRAY_SIZE(aic34_rx51_controls),
+ .dapm_widgets = aic34_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic34_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static int rx51_soc_probe(struct platform_device *pdev)
+{
+ struct rx51_audio_pdata *pdata;
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card = &rx51_sound_card;
+ int err;
+
+ if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900"))
+ return -ENODEV;
+
+ card->dev = &pdev->dev;
+
+ if (np) {
+ struct device_node *dai_node;
+
+ dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "McBSP node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_dai[0].cpu_dai_name = NULL;
+ rx51_dai[0].platform_name = NULL;
+ rx51_dai[0].cpu_of_node = dai_node;
+ rx51_dai[0].platform_of_node = dai_node;
+
+ dai_node = of_parse_phandle(np, "nokia,audio-codec", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "Codec node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_dai[0].codec_name = NULL;
+ rx51_dai[0].codec_of_node = dai_node;
+
+ dai_node = of_parse_phandle(np, "nokia,audio-codec", 1);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "Auxiliary Codec node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_aux_dev[0].codec_name = NULL;
+ rx51_aux_dev[0].codec_of_node = dai_node;
+ rx51_codec_conf[0].dev_name = NULL;
+ rx51_codec_conf[0].of_node = dai_node;
+
+ dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0);
+ if (!dai_node) {
+ dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
+ return -EINVAL;
+ }
+ rx51_aux_dev[1].codec_name = NULL;
+ rx51_aux_dev[1].codec_of_node = dai_node;
+ rx51_codec_conf[1].dev_name = NULL;
+ rx51_codec_conf[1].of_node = dai_node;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+
+ snd_soc_card_set_drvdata(card, pdata);
+
+ pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
+ "tvout-selection",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(pdata->tvout_selection_gpio)) {
+ dev_err(card->dev, "could not get tvout selection gpio\n");
+ return PTR_ERR(pdata->tvout_selection_gpio);
+ }
+
+ pdata->jack_detection_gpio = devm_gpiod_get(card->dev,
+ "jack-detection",
+ GPIOD_ASIS);
+ if (IS_ERR(pdata->jack_detection_gpio)) {
+ dev_err(card->dev, "could not get jack detection gpio\n");
+ return PTR_ERR(pdata->jack_detection_gpio);
+ }
+
+ pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(pdata->eci_sw_gpio)) {
+ dev_err(card->dev, "could not get eci switch gpio\n");
+ return PTR_ERR(pdata->eci_sw_gpio);
+ }
+
+ pdata->speaker_amp_gpio = devm_gpiod_get(card->dev,
+ "speaker-amplifier",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(pdata->speaker_amp_gpio)) {
+ dev_err(card->dev, "could not get speaker enable gpio\n");
+ return PTR_ERR(pdata->speaker_amp_gpio);
+ }
+
+ err = devm_snd_soc_register_card(card->dev, card);
+ if (err) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rx51_audio_of_match[] = {
+ { .compatible = "nokia,n900-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rx51_audio_of_match);
+#endif
+
+static struct platform_driver rx51_soc_driver = {
+ .driver = {
+ .name = "rx51-audio",
+ .of_match_table = of_match_ptr(rx51_audio_of_match),
+ },
+ .probe = rx51_soc_probe,
+};
+
+module_platform_driver(rx51_soc_driver);
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ALSA SoC Nokia RX-51");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rx51-audio");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include <linux/omap-dmaengine.h>
+
+#include "sdma-pcm.h"
+
+static const struct snd_pcm_hardware sdma_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .period_bytes_min = 32,
+ .period_bytes_max = 64 * 1024,
+ .buffer_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 255,
+};
+
+static const struct snd_dmaengine_pcm_config sdma_dmaengine_pcm_config = {
+ .pcm_hardware = &sdma_pcm_hardware,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .compat_filter_fn = omap_dma_filter_fn,
+ .prealloc_buffer_size = 128 * 1024,
+};
+
+int sdma_pcm_platform_register(struct device *dev,
+ char *txdmachan, char *rxdmachan)
+{
+ struct snd_dmaengine_pcm_config *config;
+ unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT;
+
+ /* Standard names for the directions: 'tx' and 'rx' */
+ if (!txdmachan && !rxdmachan)
+ return devm_snd_dmaengine_pcm_register(dev,
+ &sdma_dmaengine_pcm_config,
+ flags);
+
+ config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ *config = sdma_dmaengine_pcm_config;
+
+ if (!txdmachan || !rxdmachan) {
+ /* One direction only PCM */
+ flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX;
+ if (!txdmachan) {
+ txdmachan = rxdmachan;
+ rxdmachan = NULL;
+ }
+ }
+
+ config->chan_names[0] = txdmachan;
+ config->chan_names[1] = rxdmachan;
+
+ return devm_snd_dmaengine_pcm_register(dev, config, flags);
+}
+EXPORT_SYMBOL_GPL(sdma_pcm_platform_register);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_DESCRIPTION("sDMA PCM ASoC platform driver");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#ifndef __SDMA_PCM_H__
+#define __SDMA_PCM_H__
+
+#if IS_ENABLED(CONFIG_SND_SOC_TI_SDMA_PCM)
+int sdma_pcm_platform_register(struct device *dev,
+ char *txdmachan, char *rxdmachan);
+#else
+static inline int sdma_pcm_platform_register(struct device *dev,
+ char *txdmachan, char *rxdmachan)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_SND_SOC_TI_SDMA_PCM */
+
+#endif /* __SDMA_PCM_H__ */