From d9d9621338b9779caa960ad4ec8699fac5c33152 Mon Sep 17 00:00:00 2001 From: Nitin Pai Date: Fri, 29 Jul 2011 17:07:45 +0530 Subject: tegra: alsa: Add support for generic dit codec Add support for Generic Codec Driver Add support for Generic Codec SOC Driver Signed-off-by: Nitin Pai Change-Id: I3fbe2fbe8a559dbfa1de6d73bec0c5035681051a Reviewed-on: http://git-master/r/43946 Tested-by: Nitin Pai Reviewed-by: Sumit Bhattacharya Reviewed-by: Scott Peterson Reviewed-by: Songhee Baek --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/generic_codec.c | 180 ++++++++++++++++++++++ sound/soc/codecs/generic_codec.h | 24 +++ sound/soc/tegra/Makefile | 1 + sound/soc/tegra/tegra_soc_generic_codec.c | 248 ++++++++++++++++++++++++++++++ 6 files changed, 459 insertions(+) create mode 100644 sound/soc/codecs/generic_codec.c create mode 100644 sound/soc/codecs/generic_codec.h create mode 100644 sound/soc/tegra/tegra_soc_generic_codec.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 27cb80edf3e9..badcb751e7e1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -30,6 +30,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX9877 if I2C select SND_SOC_PCM3008 select SND_SOC_SPDIF + select SND_SOC_GENERIC_CODEC select SND_SOC_SSM2602 if I2C select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TLV320AIC23 if I2C @@ -160,6 +161,9 @@ config SND_SOC_PCM3008 config SND_SOC_SPDIF tristate +config SND_SOC_GENERIC_CODEC + tristate "Generic Codec" + config SND_SOC_SSM2602 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1107e41eca98..0709f9ce9bf5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -17,6 +17,7 @@ snd-soc-l3-objs := l3.o snd-soc-max98088-objs := max98088.o snd-soc-pcm3008-objs := pcm3008.o snd-soc-spdif-objs := spdif_transciever.o +snd-soc-generic-codec-objs := generic_codec.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-stac9766-objs := stac9766.o snd-soc-tlv320aic23-objs := tlv320aic23.o @@ -87,6 +88,7 @@ obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif.o +obj-$(CONFIG_SND_SOC_GENERIC_CODEC) += snd-soc-generic-codec.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o diff --git a/sound/soc/codecs/generic_codec.c b/sound/soc/codecs/generic_codec.c new file mode 100644 index 000000000000..14954790879d --- /dev/null +++ b/sound/soc/codecs/generic_codec.c @@ -0,0 +1,180 @@ +/* + * ALSA SoC Generic Codec driver + * + * This Codec can be used for the following purposes + * S/PDIF/HDMI audio where there is no codec. + * Audio over MOST (where CPLD logic is used) + * Complex SOCs shipping their Drivers without Codec Support + * + * Author: Nitin Pai, + * Copyright (c) 2009-2010, NVIDIA Corporation. + * + * + * Based on code copyright/by: + * + * Author: Steve Chen, + * Copyright: (C) 2009 MontaVista Software, Inc., + * 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 +#include +#include +#include +#include +#include + +#include "generic_codec.h" + +MODULE_LICENSE("GPL"); + +#define STUB_RATES SNDRV_PCM_RATE_8000_96000 +#define STUB_FORMATS SNDRV_PCM_FMTBIT_S16_LE + +static struct snd_soc_codec *generic_dit_codec; + +static int generic_dit_codec_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret; + + if (generic_dit_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + } + + socdev->card->codec = generic_dit_codec; + codec = generic_dit_codec; + + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + dev_err(codec->dev, "failed to create pcms: %d\n", ret); + goto err_create_pcms; + } + + return 0; + +err_create_pcms: + return ret; +} + +static int generic_dit_codec_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + snd_soc_free_pcms(socdev); + + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_generic_dit = { + .probe = generic_dit_codec_probe, + .remove = generic_dit_codec_remove, +}; EXPORT_SYMBOL_GPL(soc_codec_dev_generic_dit); + +struct snd_soc_dai generic_dit_stub_dai = { + .name = "GENERIC-DIT", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 384, + .rates = STUB_RATES, + .formats = STUB_FORMATS, + }, +}; +EXPORT_SYMBOL_GPL(generic_dit_stub_dai); + +static int generic_dit_probe(struct platform_device *pdev) +{ + struct snd_soc_codec *codec; + int ret; + + if (generic_dit_codec) { + dev_err(&pdev->dev, "Another Codec is registered\n"); + ret = -EINVAL; + goto err_reg_codec; + } + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + codec->dev = &pdev->dev; + + mutex_init(&codec->mutex); + + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->name = "generic-dit"; + codec->owner = THIS_MODULE; + codec->dai = &generic_dit_stub_dai; + codec->num_dai = 1; + + generic_dit_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto err_reg_codec; + } + + generic_dit_stub_dai.dev = &pdev->dev; + ret = snd_soc_register_dai(&generic_dit_stub_dai); + if (ret < 0) { + dev_err(codec->dev, "Failed to register dai: %d\n", ret); + goto err_reg_dai; + } + + return 0; + +err_reg_dai: + snd_soc_unregister_codec(codec); +err_reg_codec: + kfree(generic_dit_codec); + return ret; +} + +static int generic_dit_remove(struct platform_device *pdev) +{ + snd_soc_unregister_dai(&generic_dit_stub_dai); + snd_soc_unregister_codec(generic_dit_codec); + kfree(generic_dit_codec); + generic_dit_codec = NULL; + return 0; +} + +static struct platform_driver generic_dit_driver = { + .probe = generic_dit_probe, + .remove = generic_dit_remove, + .driver = { + .name = "generic-dit", + .owner = THIS_MODULE, + }, +}; + +static int __init generic_dit_modinit(void) +{ + return platform_driver_register(&generic_dit_driver); +} + +static void __exit generic_dit_exit(void) +{ + platform_driver_unregister(&generic_dit_driver); +} + +module_init(generic_dit_modinit); +module_exit(generic_dit_exit); + diff --git a/sound/soc/codecs/generic_codec.h b/sound/soc/codecs/generic_codec.h new file mode 100644 index 000000000000..7d0ded313223 --- /dev/null +++ b/sound/soc/codecs/generic_codec.h @@ -0,0 +1,24 @@ +/* + * ALSA SoC DIT/DIR driver header + * + * Author: Nitin Pai, + * Copyright (c) 2009-2010, NVIDIA Corporation. + * + * + * Based on code copyright/by: + * + * Author: Steve Chen, + * Copyright: (C) 2008 MontaVista Software, Inc., + * + * 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 CODEC_STUBS_H +#define CODEC_STUBS_H + +extern struct snd_soc_codec_device soc_codec_dev_generic_dit; +extern struct snd_soc_dai generic_dit_stub_dai; + +#endif /* CODEC_STUBS_H */ diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index a0a3958ee2dd..369e8c4c15fd 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_SND_SOC_WM8903) += tegra_soc_wm8903.o obj-$(CONFIG_SND_SOC_WM8753) += tegra_soc_wm8753.o obj-$(CONFIG_SND_SOC_MAX98088) += tegra_soc_max98088.o obj-$(CONFIG_SND_SOC_AD193X) += tegra_soc_ad193x.o +obj-$(CONFIG_SND_SOC_GENERIC_CODEC) += tegra_soc_generic_codec.o diff --git a/sound/soc/tegra/tegra_soc_generic_codec.c b/sound/soc/tegra/tegra_soc_generic_codec.c new file mode 100644 index 000000000000..343e834d02a4 --- /dev/null +++ b/sound/soc/tegra/tegra_soc_generic_codec.c @@ -0,0 +1,248 @@ +/* + * tegra_soc_generic_codec.c -- SoC audio for tegra + * + * (c) 2010-2011 Nvidia Graphics Pvt. Ltd. + * http://www.nvidia.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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "tegra_soc.h" +#include "../codecs/generic_codec.h" + +static struct platform_device *tegra_snd_device; + +extern struct snd_soc_dai tegra_i2s_dai[]; +extern struct snd_soc_platform tegra_soc_platform; + +static int tegra_hifi_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->dai->cpu_dai; + int dai_flag = 0, sys_clk; + int err; + + enum dac_dap_data_format data_fmt; + + /* Program the cpu_dai only */ + + data_fmt = dac_dap_data_format_i2s; + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + data_fmt = tegra_das_get_codec_data_fmt(tegra_audio_codec_type_hifi); + if (tegra_das_is_port_master(tegra_audio_codec_type_hifi)) + dai_flag |= SND_SOC_DAIFMT_CBM_CFM; + else +#endif + dai_flag |= SND_SOC_DAIFMT_CBS_CFS; + + if ((data_fmt & dac_dap_data_format_tdm)) + dai_flag |= SND_SOC_DAIFMT_DSP_A; + else if ((data_fmt & dac_dap_data_format_rjm)) + dai_flag |= SND_SOC_DAIFMT_RIGHT_J; + else if ((data_fmt & dac_dap_data_format_i2s)) + dai_flag |= SND_SOC_DAIFMT_I2S; + else if ((data_fmt & dac_dap_data_format_ljm)) + dai_flag |= SND_SOC_DAIFMT_LEFT_J; + + + err = snd_soc_dai_set_fmt(cpu_dai, dai_flag); + if (err < 0) { + pr_err("cpu_dai fmt not set\n"); + return err; + } + + sys_clk = 48000 * 512; + err = snd_soc_dai_set_sysclk(cpu_dai, 0, sys_clk, SND_SOC_CLOCK_IN); + if (err < 0) { + pr_err("cpu_dai clock not set\n"); + return err; + } + + return 0; +} + +void tegra_ext_control(struct snd_soc_codec *codec, int new_con) +{ + return; +} + +int tegra_codec_startup(struct snd_pcm_substream *substream) +{ + tegra_das_power_mode(true); + return 0; +} + +void tegra_codec_shutdown(struct snd_pcm_substream *substream) +{ +} + +int tegra_soc_suspend_pre(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +int tegra_soc_suspend_post(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +int tegra_soc_resume_pre(struct platform_device *pdev) +{ + return 0; +} + +int tegra_soc_resume_post(struct platform_device *pdev) +{ + return 0; +} + +static struct snd_soc_ops tegra_hifi_ops = { + .hw_params = tegra_hifi_hw_params, + .startup = tegra_codec_startup, + .shutdown = tegra_codec_shutdown, +}; + +static int tegra_codec_init(struct snd_soc_codec *codec) +{ + struct tegra_audio_data *audio_data = codec->socdev->codec_data; + int err = 0; + + if (!audio_data->init_done) { + + err = tegra_das_open(); + if (err) { + pr_err("Failed get dap mclk\n"); + err = -ENODEV; + goto generic_codec_init_fail; + } + + err = tegra_das_enable_mclk(); + if (err) { + pr_err("Failed to enable dap mclk\n"); + err = -ENODEV; + goto generic_codec_init_fail; + } + + err = tegra_controls_init(codec); + if (err < 0) { + pr_err("Failed in controls init\n"); + goto generic_codec_init_fail; + } + + audio_data->codec = codec; + audio_data->init_done = 1; + } + + return err; + +generic_codec_init_fail: + + tegra_das_disable_mclk(); + tegra_das_close(); + return err; +} + +#define TEGRA_CREATE_SOC_DAI_LINK(xname, xstreamname, \ + xcpudai, xcodecdai, xops) \ +{ \ + .name = xname, \ + .stream_name = xstreamname, \ + .cpu_dai = xcpudai, \ + .codec_dai = xcodecdai, \ + .init = tegra_codec_init, \ + .ops = xops, \ +} + +/* Currently both the DAI link to the same CODEC dai + * as only one of them can be active at the same time + * */ +static struct snd_soc_dai_link tegra_soc_dai[] = { + TEGRA_CREATE_SOC_DAI_LINK("Generic DIT Codec", "Multi-8", + &tegra_i2s_dai[0], &generic_dit_stub_dai, + &tegra_hifi_ops), + + TEGRA_CREATE_SOC_DAI_LINK("Generic DIT Codec", "Multi-16", + &tegra_i2s_dai[1], + &generic_dit_stub_dai, &tegra_hifi_ops), +}; + +static struct tegra_audio_data audio_data = { + .init_done = 0, + .play_device = TEGRA_AUDIO_DEVICE_NONE, + .capture_device = TEGRA_AUDIO_DEVICE_NONE, + .is_call_mode = false, + .codec_con = TEGRA_AUDIO_OFF, +}; + +static struct snd_soc_card tegra_snd_soc = { + .name = "tegra", + .platform = &tegra_soc_platform, + .dai_link = tegra_soc_dai, + .num_links = ARRAY_SIZE(tegra_soc_dai), + .suspend_pre = tegra_soc_suspend_pre, + .suspend_post = tegra_soc_suspend_post, + .resume_pre = tegra_soc_resume_pre, + .resume_post = tegra_soc_resume_post, +}; + +static struct snd_soc_device tegra_snd_devdata = { + .card = &tegra_snd_soc, + .codec_data = &audio_data, + .codec_dev = &soc_codec_dev_generic_dit, +}; + +static int __init tegra_init(void) +{ + int ret = 0; + + tegra_snd_device = platform_device_alloc("soc-audio", -1); + if (!tegra_snd_device) { + pr_err("failed to allocate soc-audio\n"); + return -ENOMEM; + } + + platform_set_drvdata(tegra_snd_device, &tegra_snd_devdata); + tegra_snd_devdata.dev = &tegra_snd_device->dev; + + ret = platform_device_add(tegra_snd_device); + if (ret) { + pr_err("audio device could not be added\n"); + goto fail; + } + + return 0; + +fail: + if (tegra_snd_device) { + platform_device_put(tegra_snd_device); + tegra_snd_device = 0; + } + + return ret; +} + +static void __exit tegra_exit(void) +{ + platform_device_unregister(tegra_snd_device); +} + +module_init(tegra_init); +module_exit(tegra_exit); + +/* Module information */ +MODULE_DESCRIPTION("Tegra ALSA SoC for Generic Codec"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3