[alsa-devel] [PATCH 3/3] ASoC: add machine driver for i.mx27_visstrim_m10 board
This adds support for i.mx27_visstrim_sm10 board machine driver which uses an i.mx27 processor plus a wm8974 codec.
It has been tested on a visstrim_sm10 board.
Signed-off-by: Javier Martin javier.martin@vista-silicon.com --- sound/soc/imx/Kconfig | 8 + sound/soc/imx/Makefile | 4 + sound/soc/imx/mx27vis_wm8974.c | 317 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+), 0 deletions(-)
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig index 886dadd..2c6f568 100644 --- a/sound/soc/imx/Kconfig +++ b/sound/soc/imx/Kconfig @@ -9,5 +9,13 @@ config SND_MX1_MX2_SOC config SND_MXC_SOC_SSI tristate
+config SND_SOC_MX27VIS_WM8974 + tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board" + depends on SND_MX1_MX2_SOC && MACH_MX27 && MACH_IMX27_VISSTRIM_M10 + select SND_MXC_SOC_SSI + select SND_SOC_WM8974 + help + Say Y if you want to add support for SoC audio on Visstrim SM10 + board with WM8974.
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile index 6552cb2..c2ffd2c 100644 --- a/sound/soc/imx/Makefile +++ b/sound/soc/imx/Makefile @@ -4,3 +4,7 @@ snd-soc-mxc-ssi-objs := mxc-ssi.o
obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o + +# i.MX Machine Support +snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o +obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c new file mode 100644 index 0000000..e4dcb53 --- /dev/null +++ b/sound/soc/imx/mx27vis_wm8974.c @@ -0,0 +1,317 @@ +/* + * mx27vis_wm8974.c -- SoC audio for mx27vis + * + * Copyright 2009 Vista Silicon S.L. + * Author: Javier Martin + * javier.martin@vista-silicon.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. + * + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + + +#include "../codecs/wm8974.h" +#include "mx1_mx2-pcm.h" +#include "mxc-ssi.h" +#include <mach/gpio.h> +#include <mach/iomux.h> + +#define IGNORED_ARG 0 + + +static struct snd_soc_card mx27vis; + +/** + * This function connects SSI1 (HPCR1) as slave to + * SSI1 external signals (PPCR1) + * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from + * port 4 + */ +void audmux_connect_1_4(void) +{ + pr_debug("AUDMUX: normal operation mode\n"); + /* Reset HPCR1 and PPCR1 */ + + DAM_HPCR1 = 0x00000000; + DAM_PPCR1 = 0x00000000; + + /* set to synchronous */ + DAM_HPCR1 |= AUDMUX_HPCR_SYN; + DAM_PPCR1 |= AUDMUX_PPCR_SYN; + + + /* set Rx sources 1 <--> 4 */ + DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */ + DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */ + + /* set Tx frame and Clock direction and source 4 --> 1 output */ + DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR; + DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */ + + return; +} + +static int mx27vis_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 *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0; + int ret = 0; + + /* + * The WM8974 is better at generating accurate audio clocks than the + * MX27 SSI controller, so we will use it as master when we can. + */ + switch (params_rate(params)) { + case 8000: + fmt = SND_SOC_DAIFMT_CBM_CFM; + mclk = WM8974_MCLKDIV_12; + pll_out = 24576000; + break; + case 16000: + fmt = SND_SOC_DAIFMT_CBM_CFM; + pll_out = 12288000; + break; + case 48000: + fmt = SND_SOC_DAIFMT_CBM_CFM; + bclk = WM8974_BCLKDIV_4; + pll_out = 12288000; + break; + case 96000: + fmt = SND_SOC_DAIFMT_CBM_CFM; + bclk = WM8974_BCLKDIV_2; + pll_out = 12288000; + break; + case 11025: + fmt = SND_SOC_DAIFMT_CBM_CFM; + bclk = WM8974_BCLKDIV_16; + pll_out = 11289600; + break; + case 22050: + fmt = SND_SOC_DAIFMT_CBM_CFM; + bclk = WM8974_BCLKDIV_8; + pll_out = 11289600; + break; + case 44100: + fmt = SND_SOC_DAIFMT_CBM_CFM; + bclk = WM8974_BCLKDIV_4; + mclk = WM8974_MCLKDIV_2; + pll_out = 11289600; + break; + case 88200: + fmt = SND_SOC_DAIFMT_CBM_CFM; + bclk = WM8974_BCLKDIV_2; + pll_out = 11289600; + break; + } + + /* set codec DAI configuration */ + ret = codec_dai->ops->set_fmt(codec_dai, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_SYNC | fmt); + if (ret < 0) { + printk(KERN_ERR "Error from codec DAI configuration\n"); + return ret; + } + + /* set cpu DAI configuration */ + ret = cpu_dai->ops->set_fmt(cpu_dai, + SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_SYNC | fmt); + if (ret < 0) { + printk(KERN_ERR "Error from cpu DAI configuration\n"); + return ret; + } + + /* Put DC field of STCCR to 1 (not zero) */ + ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2); + + /* set the SSI system clock as input */ + ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "Error when setting system SSI clk\n"); + return ret; + } + + /* set codec BCLK division for sample rate */ + ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk); + if (ret < 0) { + printk(KERN_ERR "Error when setting BCLK division\n"); + return ret; + } + + + /* codec PLL input is 25 MHz */ + ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, + 25000000, pll_out); + if (ret < 0) { + printk(KERN_ERR "Error when setting PLL input\n"); + return ret; + } + + /*set codec MCLK division for sample rate */ + ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk); + if (ret < 0) { + printk(KERN_ERR "Error when setting MCLK division\n"); + return ret; + } + + return 0; +} + +static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + + /* disable the PLL */ + return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, 0, 0); +} + +/* + * mx27vis WM8974 HiFi DAI opserations. + */ +static struct snd_soc_ops mx27vis_hifi_ops = { + .hw_params = mx27vis_hifi_hw_params, + .hw_free = mx27vis_hifi_hw_free, +}; + + +static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int mx27vis_resume(struct platform_device *pdev) +{ + return 0; +} + +static int mx27vis_probe(struct platform_device *pdev) +{ + int ret = 0; + + ret = get_ssi_clk(0, &pdev->dev); + + if (ret < 0) { + printk(KERN_ERR "%s: cant get ssi clock\n", __func__); + return ret; + } + + + return 0; +} + +static int mx27vis_remove(struct platform_device *pdev) +{ + put_ssi_clk(0); + return 0; +} + +static struct snd_soc_dai_link mx27vis_dai[] = { +{ /* Hifi Playback*/ + .name = "WM8974", + .stream_name = "WM8974 HiFi", + .cpu_dai = &imx_ssi_pcm_dai[0], + .codec_dai = &wm8974_dai, + .ops = &mx27vis_hifi_ops, +}, +}; + +static struct snd_soc_card mx27vis = { + .name = "mx27vis", + .platform = &mx1_mx2_soc_platform, + .probe = mx27vis_probe, + .remove = mx27vis_remove, + .suspend_pre = mx27vis_suspend, + .resume_post = mx27vis_resume, + .dai_link = mx27vis_dai, + .num_links = ARRAY_SIZE(mx27vis_dai), +}; + +static struct snd_soc_device mx27vis_snd_devdata = { + .card = &mx27vis, + .codec_dev = &soc_codec_dev_wm8974, +}; + +static struct platform_device *mx27vis_snd_device; + +/* Temporal definition of board specific behaviour */ +void gpio_ssi_active(int ssi_num) +{ + int ret = 0; + + unsigned int ssi1_pins[] = { + PC20_PF_SSI1_FS, + PC21_PF_SSI1_RXD, + PC22_PF_SSI1_TXD, + PC23_PF_SSI1_CLK, + }; + unsigned int ssi2_pins[] = { + PC24_PF_SSI2_FS, + PC25_PF_SSI2_RXD, + PC26_PF_SSI2_TXD, + PC27_PF_SSI2_CLK, + }; + if (ssi_num == 0) + ret = mxc_gpio_setup_multiple_pins(ssi1_pins, + ARRAY_SIZE(ssi1_pins), "USB OTG"); + else + ret = mxc_gpio_setup_multiple_pins(ssi2_pins, + ARRAY_SIZE(ssi2_pins), "USB OTG"); + if (ret) + printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num); +} + + +static int __init mx27vis_init(void) +{ + int ret; + + mx27vis_snd_device = platform_device_alloc("soc-audio", -1); + if (!mx27vis_snd_device) + return -ENOMEM; + + platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata); + mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev; + ret = platform_device_add(mx27vis_snd_device); + + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + platform_device_put(mx27vis_snd_device); + } + + /* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */ + gpio_ssi_active(0); + audmux_connect_1_4(); + + return ret; +} + +static void __exit mx27vis_exit(void) +{ + /* We should call some "ssi_gpio_inactive()" properly */ +} + +module_init(mx27vis_init); +module_exit(mx27vis_exit); + + +MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com"); +MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis"); +MODULE_LICENSE("GPL"); ---
Hi all!
Mates, I am following the example PCM.C that I found at alsa web site. its a sine wave generator. I am looking for a control that alsa provides to control the amplitude of the wave.
As I could found so for, there is just frequency control... I didn't find any amplitude control.
* I don't want to control the volume using alsamixer or even my sound... I want to generate a wave with predefined amplitude.
Does anyone could give me a little help??
Thanks a lot!
Guilherme Longo wrote:
Hi all!
Mates, I am following the example PCM.C that I found at alsa web site. its a sine wave generator. I am looking for a control that alsa provides to control the amplitude of the wave.
As I could found so for, there is just frequency control... I didn't find any amplitude control.
This line int pcm.c is not amplitude control?
unsigned int maxval = (1 << (snd_pcm_format_width(format) - 1)) - 1;
it's an int, so you probably need to alter this a bit to allow you to have arbitrary control over the amplitude (ie: multiply by a float less than 1.0)
HTH
johnu
- I don't want to control the volume using alsamixer or even my sound...
I want to generate a wave with predefined amplitude.
Does anyone could give me a little help??
Thanks a lot!
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Hi again.
I've got half of my work done with it. I found the values of the samples been analyzed, **BUT* *it is given in a strange unity!
while (count-- > 0) { res = sin(phase) * 15000; ires.i = res; tmp = ires.c; * printf("som -> %g\n", res); // Here I get the values* for (chn = 0; chn < channels; chn++) { for (byte = 0; byte < (unsigned int)bps; byte++) *(samples[chn] + byte) = tmp[1]; samples[chn] += steps[chn]; }
Now, how can I transform this samples in a more human understandable notation as in Db unity for example? Is there any alsa api function to control it? Should I use the mixer interface somehow?
This is the output:
som -> 11976.3 som -> 11387 som -> 10752.9 som -> 10076.6 som -> 9360.7 som -> 8608.02 som -> 7821.53 som -> 7004.31 som -> 6159.56 som -> 5290.63 som -> 4400.9 som -> 3493.89 som -> 2573.15 som -> 1642.3 som -> 704.996 som -> -235.076 som -> -1174.22 som -> -2108.76 som -> -3035.01 som -> -3949.33 som -> -4848.14 som -> -5727.91
Thanks! Guilherme Longo wrote:
Hi all!
Mates, I am following the example PCM.C that I found at alsa web site. its a sine wave generator. I am looking for a control that alsa provides to control the amplitude of the wave.
As I could found so for, there is just frequency control... I didn't find any amplitude control.
- I don't want to control the volume using alsamixer or even my
sound... I want to generate a wave with predefined amplitude.
Does anyone could give me a little help??
Thanks a lot!
On Tue, Aug 04, 2009 at 05:18:02PM +0200, javier Martin wrote:
This adds support for i.mx27_visstrim_sm10 board machine driver which uses an i.mx27 processor plus a wm8974 codec.
It has been tested on a visstrim_sm10 board.
Signed-off-by: Javier Martin javier.martin@vista-silicon.com
Again, this all looks good with some relatively small nits.
+/**
- This function connects SSI1 (HPCR1) as slave to
- SSI1 external signals (PPCR1)
- As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from
- port 4
- */
+void audmux_connect_1_4(void) +{
Obviously this ought to get pulled out, either into an arch AUXMUX thing or a separate file in here. However, neither of those things exist right now. I'll just go prod the last AUXMUX thread on linux-arm-kernel.
- return;
+}
No need for the return; here.
- /*
* The WM8974 is better at generating accurate audio clocks than the
* MX27 SSI controller, so we will use it as master when we can.
*/
- switch (params_rate(params)) {
- case 8000:
Unless I'm missing something the "when we can" is "in all cases" :)
- /* set codec DAI configuration */
- ret = codec_dai->ops->set_fmt(codec_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
SND_SOC_DAIFMT_SYNC | fmt);
- if (ret < 0) {
printk(KERN_ERR "Error from codec DAI configuration\n");
return ret;
- }
Printing the value of ret would be nice.
- /* Put DC field of STCCR to 1 (not zero) */
- ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2);
Rats, you are using the TDM slot configuration. Oh well. Should check this error.
+static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state) +{
- return 0;
+}
+static int mx27vis_resume(struct platform_device *pdev) +{
- return 0;
+}
These can be omitted if empty.
- unsigned int ssi1_pins[] = {
PC20_PF_SSI1_FS,
PC21_PF_SSI1_RXD,
PC22_PF_SSI1_TXD,
PC23_PF_SSI1_CLK,
- };
- unsigned int ssi2_pins[] = {
PC24_PF_SSI2_FS,
PC25_PF_SSI2_RXD,
PC26_PF_SSI2_TXD,
PC27_PF_SSI2_CLK,
- };
- if (ssi_num == 0)
ret = mxc_gpio_setup_multiple_pins(ssi1_pins,
ARRAY_SIZE(ssi1_pins), "USB OTG");
- else
ret = mxc_gpio_setup_multiple_pins(ssi2_pins,
ARRAY_SIZE(ssi2_pins), "USB OTG");
This would normally be done under arch/arm in the board setup code.
participants (4)
-
Guilherme Longo
-
javier Martin
-
John L. Utz III
-
Mark Brown