On Fri, Dec 17, 2010 at 02:41:29PM -0700, Stephen Warren wrote: > Signed-off-by: Stephen Warren <swarren@xxxxxxxxxx> So, I actually have a one of these... If I can figure out how to get a useful kernel onto it I should be able to boot and test it. > --- > sound/soc/tegra/Kconfig | 10 ++ > sound/soc/tegra/Makefile | 4 + > sound/soc/tegra/harmony.c | 313 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 327 insertions(+), 0 deletions(-) > create mode 100644 sound/soc/tegra/harmony.c > > diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig > index d6b0444..31a310d 100644 > --- a/sound/soc/tegra/Kconfig > +++ b/sound/soc/tegra/Kconfig > @@ -17,3 +17,13 @@ config SND_TEGRA_SOC_I2S > > This option conflicts with TEGRA_I2S_AUDIO. > > +config SND_TEGRA_SOC_HARMONY > + tristate "SoC Audio support for Tegra Harmony reference board" > + depends on SND_TEGRA_SOC && MACH_HARMONY && I2C > + default m > + select SND_TEGRA_SOC_I2S > + select SND_SOC_WM8903 > + help > + Say Y or M here if you want to add support for SoC audio on the > + Tegra Harmony reference board. > + > diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile > index d7712a2..f94bce8 100644 > --- a/sound/soc/tegra/Makefile > +++ b/sound/soc/tegra/Makefile > @@ -7,3 +7,7 @@ obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o > obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o > obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o > > +# Tegra machine Support > +snd-soc-tegra-harmony-objs := harmony.o > + > +obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o > diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c > new file mode 100644 > index 0000000..158308b > --- /dev/null > +++ b/sound/soc/tegra/harmony.c > @@ -0,0 +1,313 @@ > +/* > + * harmony.c - Harmony machine ASoC driver > + * > + * Author: Stephen Warren <swarren@xxxxxxxxxx> > + * Copyright (C) 2010 - NVIDIA, Inc. > + * > + * Based on code copyright/by: > + * > + * Copyright (c) 2009-2010, NVIDIA Corporation. > + * Scott Peterson <speterson@xxxxxxxxxx> > + * Vijay Mali <vmali@xxxxxxxxxx> > + * > + * Copyright (C) 2010 Google, Inc. > + * Iliyan Malchev <malchev@xxxxxxxxxx> > + * > + * 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 <asm/mach-types.h> > +#include <linux/clk.h> > +#include <linux/module.h> > +#include <sound/core.h> > +#include <sound/pcm.h> > +#include <sound/pcm_params.h> > +#include <sound/soc-dapm.h> > +#include <sound/soc.h> > + > +#include "../codecs/wm8903.h" > +#include "tegra_das.h" > +#include "tegra_i2s.h" > +#include "tegra_pcm.h" > + > +#define PREFIX "ASoC Harmony: " > + > +static struct platform_device *harmony_snd_device; > + > +struct clk *clk_pll_a; > +struct clk *clk_pll_a_out0; > +struct clk *clk_i2s1; > +struct clk *clk_audio; > +struct clk *clk_audio_2x; Lots of direct fiddling with clocks here. It feels like a lot of this should be abstracted out of the machine drivers - the WM8903 is a fairly straightforward CODEC from the system integration point of view, 99% of single DAI CODECs are going to require identical configuration, and much of the clocking configuration looks like it's actually directed at the I2S controller and should be handled there. > + > +static int harmony_asoc_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->dai->codec_dai; > + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; > + int srate, bitclock; > + int err; > + > + srate = params_rate(params); > + switch (srate) { > + case 11025: > + case 22050: > + case 44100: > + case 88200: > + bitclock = 11289600; > + break; > + case 8000: > + case 16000: > + case 32000: > + case 48000: > + case 64000: > + case 96000: > + bitclock = 12288000; > + break; These seem remarkably high bitclocks for most of the sample rates - even for 88.2k and 96k these are 128fs. Some documentation as to what's going on here would be useful. > + default: > + return -EINVAL; > + } > + clk_disable(clk_audio_2x); > + clk_disable(clk_audio); > + clk_disable(clk_i2s1); > + clk_disable(clk_pll_a_out0); > + clk_disable(clk_pll_a); This is going to glitch the clock even if no change is being made. > + > + err = clk_set_rate(clk_pll_a, bitclock); > + if (err) { > + pr_err(PREFIX "Can't set pll_a rate: %d\n", err); > + return err; > + } > + > + err = clk_set_rate(clk_pll_a_out0, bitclock); > + if (err) { > + pr_err(PREFIX "Can't set pll_a_out0 rate: %d\n", err); > + return err; > + } > + > + err = clk_set_rate(clk_i2s1, bitclock); > + if (err) { > + pr_err(PREFIX "Can't set i2s1 rate: %d\n", err); > + return err; > + } > + > + /* Don't set audio/audio_2x rate; they're locked to pll_a_out0 */ > + > + err = clk_enable(clk_pll_a); > + if (err) { > + pr_err(PREFIX "Can't enable pll_a: %d\n", err); > + return err; > + } > + > + err = clk_enable(clk_pll_a_out0); > + if (err) { > + pr_err(PREFIX "Can't enable pll_a_out0: %d\n", err); > + return err; > + } > + > + err = clk_enable(clk_i2s1); > + if (err) { > + pr_err(PREFIX "Can't enable i2s1: %d\n", err); > + return err; > + } > + > + err = clk_enable(clk_audio); > + if (err) { > + pr_err(PREFIX "Can't enable audio: %d\n", err); > + return err; > + } > + > + err = clk_enable(clk_audio_2x); > + if (err) { > + pr_err(PREFIX "Can't enable audio_2x: %d\n", err); > + return err; > + } > + > + err = snd_soc_dai_set_sysclk(codec_dai, 0, bitclock, SND_SOC_CLOCK_IN); > + if (err < 0) { > + pr_err(PREFIX "codec_dai clock not set\n"); > + return err; > + } > + err = snd_soc_dai_set_sysclk(cpu_dai, 0, bitclock, SND_SOC_CLOCK_IN); > + if (err < 0) { > + pr_err(PREFIX "cpu_dai clock not set\n"); > + return err; > + } Ah, this isn't bitclock, it's MCLK for the CODEC... > + err = snd_soc_dai_set_fmt(codec_dai, > + SND_SOC_DAIFMT_I2S | \ > + SND_SOC_DAIFMT_NB_NF | \ > + SND_SOC_DAIFMT_CBS_CFS); No need for continuation characters here. > + if (err < 0) { > + pr_err(PREFIX "codec_dai fmt not set\n"); > + return err; > + } > + > + err = snd_soc_dai_set_fmt(cpu_dai, > + SND_SOC_DAIFMT_I2S | \ > + SND_SOC_DAIFMT_NB_NF | \ > + SND_SOC_DAIFMT_CBS_CFS); > + if (err < 0) { > + pr_err(PREFIX "cpu_dai fmt not set\n"); > + return err; > + } > + > + return 0; > +} > + > +static struct snd_soc_ops harmony_asoc_ops = { > + .hw_params = harmony_asoc_hw_params, > +}; > + > +static struct snd_soc_dai_link harmony_wm8903_dai = { > + .name = "WM8903", > + .stream_name = "WM8903 PCM", > + .cpu_dai = &tegra_i2s_dai[0], > + .codec_dai = &wm8903_dai, > + .ops = &harmony_asoc_ops, > +}; > + > +static struct snd_soc_card snd_soc_harmony = { > + .name = "tegra-harmony", > + .platform = &tegra_soc_platform, > + .dai_link = &harmony_wm8903_dai, > + .num_links = 1, > +}; > + > +static struct snd_soc_device harmony_wm8903_snd_devdata = { > + .card = &snd_soc_harmony, > + .codec_dev = &soc_codec_dev_wm8903, > +}; > + > +static int __init harmony_soc_modinit(void) > +{ > + int ret; > + > + if (!machine_is_harmony()) { > + pr_err("Harmony ASoC: Not running on Tegra Harmony!\n"); > + return -ENODEV; > + } > + > + clk_pll_a = clk_get_sys(NULL, "pll_a"); > + if (IS_ERR_OR_NULL(clk_pll_a)) { > + pr_err("Can't retrieve clk pll_a\n"); > + ret = PTR_ERR(clk_pll_a); > + goto err_clock_put; > + } > + > + clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); > + if (IS_ERR_OR_NULL(clk_pll_a_out0)) { > + pr_err("Can't retrieve clk pll_a_out0\n"); > + ret = PTR_ERR(clk_pll_a_out0); > + goto err_clock_put; > + } > + > + clk_i2s1 = clk_get_sys("tegra-i2s.0", NULL); > + if (IS_ERR_OR_NULL(clk_i2s1)) { > + pr_err("Can't retrieve clk i2s1\n"); > + ret = PTR_ERR(clk_i2s1); > + goto err_clock_put; > + } > + > + clk_audio = clk_get_sys(NULL, "audio"); > + if (IS_ERR_OR_NULL(clk_audio)) { > + pr_err("Can't retrieve clk audio\n"); > + ret = PTR_ERR(clk_audio); > + goto err_clock_put; > + } > + > + clk_audio_2x = clk_get_sys(NULL, "audio_2x"); > + if (IS_ERR_OR_NULL(clk_audio_2x)) { > + pr_err("Can't retrieve clk audio_2x\n"); > + ret = PTR_ERR(clk_audio_2x); > + goto err_clock_put; > + } > + > + /* IDs 0..n-1, params 1..n */ > + ret = tegra_das_configure_dap_for_dac(0, TEGRA_DAS_DAP_SEL_DAC1); > + if (ret) { > + pr_err("Harmony ASoC: tegra_das_configure_dap_for_dac failed\n"); > + goto err_clock_put; > + } > + > + ret = tegra_das_configure_dac(0, TEGRA_DAS_DAC_SEL_DAP1); > + if (ret) { > + pr_err("Harmony ASoC: tegra_das_configure_dac failed\n"); > + goto err_clock_put; > + } > + > + /* > + * Create and register platform device > + */ > + harmony_snd_device = platform_device_alloc("soc-audio", -1); > + if (harmony_snd_device == NULL) { > + pr_err("Harmony ASoC: platform_device_alloc failed\n"); > + ret = -ENOMEM; > + goto err_device_put; > + } > + > + platform_set_drvdata(harmony_snd_device, &harmony_wm8903_snd_devdata); > + harmony_wm8903_snd_devdata.dev = &harmony_snd_device->dev; > + > + ret = platform_device_add(harmony_snd_device); > + if (ret) { > + pr_err("Harmony ASoC: platform_device_add failed (%d)\n", > + ret); > + goto err_device_put; > + } > + > + return 0; > + > +err_device_put: > + if (harmony_snd_device != NULL) { > + platform_device_put(harmony_snd_device); > + harmony_snd_device = NULL; > + } > +err_clock_put: > + if (!IS_ERR_OR_NULL(clk_audio_2x)) > + clk_put(clk_audio_2x); > + if (!IS_ERR_OR_NULL(clk_audio)) > + clk_put(clk_audio); > + if (!IS_ERR_OR_NULL(clk_i2s1)) > + clk_put(clk_i2s1); > + if (!IS_ERR_OR_NULL(clk_pll_a_out0)) > + clk_put(clk_pll_a_out0); > + if (!IS_ERR_OR_NULL(clk_pll_a)) > + clk_put(clk_pll_a); > + return ret; > +} > +module_init(harmony_soc_modinit); > + > +static void __exit harmony_soc_modexit(void) > +{ > + platform_device_unregister(harmony_snd_device); > + > + clk_put(clk_audio_2x); > + clk_put(clk_audio); > + clk_put(clk_i2s1); > + clk_put(clk_pll_a_out0); > + clk_put(clk_pll_a); > + > + harmony_snd_device = NULL; > +} > +module_exit(harmony_soc_modexit); > + > +MODULE_AUTHOR("Stephen Warren <swarren@xxxxxxxxxx>"); > +MODULE_DESCRIPTION("Harmony machine ASoC driver"); > +MODULE_LICENSE("GPL"); > -- > 1.7.0.4 > nvpublic > -- > To unsubscribe from this list: send the line "unsubscribe linux-tegra" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-tegra" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html