[alsa-devel] wm8974 and sam9g45 atmel
Hello everybody, I'm starting a new development with a Atmel SAM9G45 and a WM8974.
I'm using kernel 2.6.33.2
I have connected the SSC1 pin TD,TF,TK and the RD pin, so I want to use WM8974 like a slave.
I wrote a interface driver based on sam9263_wm8731.c
The driver seem to load ok, I recognize the codec but when I start a mp3 playing (I use mpg123) the playback not start.
Putting some dedug messagge the driver not call the function "set_bias_level", and call the mute function with mute=0. Testing the MCLK signal I not see any clock on pin because I never call clk_enable!!
There is a way to identify the problem? Someone could help me?
Thank you in advance Michele Da Rold
I put the code of driver (don't care about printk I used it for debug purpose!!)
/* * sam9g45_wm8974 -- SoC audio for AT91SAM9G20-based * ATMEL AT91SAM9G20ek board. * * Copyright (C) 2005 SAN People * Copyright (C) 2008 Atmel * * Authors: Sedji Gaouaou sedji.gaouaou@atmel.com * * Based on sam9263_wm8731.c. * Frank Mandarino fmandarino@endrelia.com * Copyright 2006 Endrelia Technologies Inc. * Based on corgi.c by: * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * * 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/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/clk.h> #include <linux/timer.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/i2c.h>
#include <linux/atmel-ssc.h>
#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h>
#include <asm/mach-types.h> #include <mach/hardware.h> #include <mach/gpio.h>
#include "../codecs/wm8974.h" #include "atmel-pcm.h" #include "atmel_ssc_dai.h"
#define MCLK_RATE 12000000
/* * As shipped the board does not have inputs. However, it is relatively * straightforward to modify the board to hook them up so support is left * in the driver. */ #undef ENABLE_MIC_INPUT #define ENABLE_MIC_INPUT 1 static struct clk *mclk;
static int at91sam9g45ek_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 ret; printk(KERN_DEBUG "at91sam9g45_wm8974 " ": at91sam9g45ek_hw_params() called\n"); /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS/*SND_SOC_DAIFMT_CBM_CFM*/); if (ret < 0) return ret;
/* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS/*SND_SOC_DAIFMT_CBM_CFM*/); if (ret < 0) return ret;
return 0; }
static struct snd_soc_ops at91sam9g45ek_ops = { .hw_params = at91sam9g45ek_hw_params, };
static int at91sam9g45ek_set_bias_level(struct snd_soc_card *card, enum snd_soc_bias_level level) { static int mclk_on; int ret = 0;
printk(KERN_DEBUG "at91sam9g45_wm8974 " ": at91sam9g45ek_set_bias_level() called %d\n",level); switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: if (!mclk_on) ret = clk_enable(mclk); if (ret == 0) mclk_on = 1; break;
case SND_SOC_BIAS_OFF: case SND_SOC_BIAS_STANDBY: if (mclk_on) clk_disable(mclk); mclk_on = 0; break; }
return ret; } #if 0 static const struct snd_soc_dapm_widget at91sam9g45ek_dapm_widgets[] = { SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), };
static const struct snd_soc_dapm_route intercon[] = {
/* Speaker output mixer */ {"Speaker Mixer", "PCM Playback Switch", "DAC"}, {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, /* speaker connected to SPKOUTP */ {"SpkN Out", NULL, "Speaker Mixer"}, {"SpkP Out", NULL, "Speaker Mixer"}, {"SPKOUTN", NULL, "SpkN Out"}, {"SPKOUTP", NULL, "SpkP Out"}, /* Input PGA */ {"Input PGA", "Aux Switch", "Aux Input"}, {"Input PGA", "MicN Switch", "MICN"}, {"Input PGA", "MicP Switch", "MICP"}, };
/* * Logic for a wm974 as connected on a at91sam9g45ek board. */ static int at91sam9g45ek_wm8974_init(struct snd_soc_codec *codec) { struct snd_soc_dai *codec_dai = &codec->dai[0]; int ret;
printk(KERN_DEBUG "at91sam9g45_wm8974 " ": at91sam9g45_wm8974_init() called\n"); /* ret = snd_soc_dai_set_sysclk(codec_dai, 0, MCLK_RATE, SND_SOC_CLOCK_IN); if (ret < 0) { printk(KERN_ERR "Failed to set WM8974 SYSCLK: %d\n", ret); return ret; } */ /* Add specific widgets */ snd_soc_dapm_new_controls(codec, at91sam9g45ek_dapm_widgets, ARRAY_SIZE(at91sam9g45ek_dapm_widgets)); /* Set up specific audio path interconnects */ snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); /* not connected */ snd_soc_dapm_nc_pin(codec, "MONOOUT"); snd_soc_dapm_nc_pin(codec, "AUX");
#ifdef ENABLE_MIC_INPUT snd_soc_dapm_enable_pin(codec, "Int Mic"); #else snd_soc_dapm_nc_pin(codec, "Int Mic"); #endif
/* always connected */ snd_soc_dapm_enable_pin(codec, "Ext Spk");
snd_soc_dapm_sync(codec);
return 0; } #endif static struct snd_soc_dai_link at91sam9g45ek_dai = { .name = "WM8974", .stream_name = "WM8974 PCM", .cpu_dai = &atmel_ssc_dai[1], .codec_dai = &wm8974_dai, #if 0 .init = at91sam9g45ek_wm8974_init, #endif .ops = &at91sam9g45ek_ops, };
static struct snd_soc_card snd_soc_at91sam9g45ek = { .name = "AT91SAMG45", .platform = &atmel_soc_platform, .dai_link = &at91sam9g45ek_dai, .num_links = 1, .set_bias_level = at91sam9g45ek_set_bias_level, };
static struct snd_soc_device at91sam9g45ek_snd_devdata = { .card = &snd_soc_at91sam9g45ek, .codec_dev = &soc_codec_dev_wm8974, };
static struct platform_device *at91sam9g45ek_snd_device;
static int __init at91sam9g45ek_init(void) { struct atmel_ssc_info *ssc_p = at91sam9g45ek_dai.cpu_dai->private_data; struct ssc_device *ssc = NULL; struct clk *main; int ret;
at91_set_A_periph(AT91_PIN_PD27, 0); /* * Codec MCLK is supplied by PCK1 - set it up. */ mclk = clk_get(NULL, "pck1"); if (IS_ERR(mclk)) { printk(KERN_ERR "ASoC: Failed to get MCLK\n"); ret = PTR_ERR(mclk); goto err; }
main = clk_get(NULL, "main"); if (IS_ERR(main)) { printk(KERN_ERR "ASoC: Failed to get PLLB\n"); ret = PTR_ERR(mclk); goto err_mclk; } ret = clk_set_parent(mclk, main); clk_put(main); if (ret != 0) { printk(KERN_ERR "ASoC: Failed to set MCLK parent\n"); goto err_mclk; }
clk_set_rate(mclk, MCLK_RATE); /* * Request SSC device */ ssc = ssc_request(1); if (IS_ERR(ssc)) { printk(KERN_ERR "ASoC: Failed to request SSC 1\n"); ret = PTR_ERR(ssc); ssc = NULL; goto err_ssc; } ssc_p->ssc = ssc;
at91sam9g45ek_snd_device = platform_device_alloc("soc-audio", -1); if (!at91sam9g45ek_snd_device) { printk(KERN_ERR "ASoC: Platform device allocation failed\n"); ret = -ENOMEM; }
platform_set_drvdata(at91sam9g45ek_snd_device, &at91sam9g45ek_snd_devdata); at91sam9g45ek_snd_devdata.dev = &at91sam9g45ek_snd_device->dev;
ret = platform_device_add(at91sam9g45ek_snd_device); if (ret) { printk(KERN_ERR "ASoC: Platform device allocation failed\n"); platform_device_put(at91sam9g45ek_snd_device); }
return ret;
err_ssc: ssc_free(ssc); ssc_p->ssc = NULL; err_mclk: clk_put(mclk); mclk = NULL; err: return ret; }
static void __exit at91sam9g45ek_exit(void) { struct atmel_ssc_info *ssc_p = at91sam9g45ek_dai.cpu_dai->private_data; struct ssc_device *ssc;
if (ssc_p != NULL) { ssc = ssc_p->ssc; if (ssc != NULL) ssc_free(ssc); ssc_p->ssc = NULL; }
platform_device_unregister(at91sam9g45ek_snd_device); at91sam9g45ek_snd_device = NULL; clk_put(mclk); mclk = NULL; }
module_init(at91sam9g45ek_init); module_exit(at91sam9g45ek_exit);
/* Module information */ MODULE_AUTHOR("Sedji Gaouaou sedji.gaouaou@atmel.com"); MODULE_DESCRIPTION("ALSA SoC AT91SAM9G45_WM8974"); MODULE_LICENSE("GPL");
On Thu, Jun 09, 2011 at 05:11:48PM +0200, Michele Da Rold wrote:
Hello everybody, I'm starting a new development with a Atmel SAM9G45 and a WM8974.
Please always CC maintainers on mails - traffic on the mailing list is easily lost.
I'm using kernel 2.6.33.2
This kernel is *very* old - I would strongly recommend updating to a more current kernel if you're looking for support.
Putting some dedug messagge the driver not call the function "set_bias_level", and call the mute function with mute=0. Testing the MCLK signal I not see any clock on pin because I never call clk_enable!!
Have you routed audio from the DAI to an output using the ALSA controls? If you haven't connected an output path up then the kernel won't power anything on.
Il 10/06/2011 12:31, Mark Brown ha scritto:
On Thu, Jun 09, 2011 at 05:11:48PM +0200, Michele Da Rold wrote:
Hello everybody, I'm starting a new development with a Atmel SAM9G45 and a WM8974.
Please always CC maintainers on mails - traffic on the mailing list is easily lost.
I'm using kernel 2.6.33.2
This kernel is *very* old - I would strongly recommend updating to a more current kernel if you're looking for support.
Updated to 2.6.39.1!
Putting some dedug messagge the driver not call the function "set_bias_level", and call the mute function with mute=0. Testing the MCLK signal I not see any clock on pin because I never call clk_enable!!
Have you routed audio from the DAI to an output using the ALSA controls? If you haven't connected an output path up then the kernel won't power anything on.
Now I have configured wm8974 like slave, the ssc generate BCLK e FRAME signal and MCLK but I haven't output (I'm doing a playback) I'm new to alsa development so I haven't understand how to route audio to DAI from output, could you help me?
Best regards
_______________________________________________
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Il 17/06/2011 11:22, Michele Da Rold ha scritto:
Il 10/06/2011 12:31, Mark Brown ha scritto:
On Thu, Jun 09, 2011 at 05:11:48PM +0200, Michele Da Rold wrote:
Hello everybody, I'm starting a new development with a Atmel SAM9G45 and a WM8974.
Please always CC maintainers on mails - traffic on the mailing list is easily lost.
I'm using kernel 2.6.33.2
This kernel is *very* old - I would strongly recommend updating to a more current kernel if you're looking for support.
Updated to 2.6.39.1!
Putting some dedug messagge the driver not call the function "set_bias_level", and call the mute function with mute=0. Testing the MCLK signal I not see any clock on pin because I never call clk_enable!!
Have you routed audio from the DAI to an output using the ALSA controls? If you haven't connected an output path up then the kernel won't power anything on.
Now I have configured wm8974 like slave, the ssc generate BCLK e FRAME signal and MCLK but I haven't output (I'm doing a playback) I'm new to alsa development so I haven't understand how to route audio to DAI from output, could you help me?
UPDATE!!!!!!! Using amixer I have put to on the numid=61,iface=MIXER,name='Speaker Mixer PCM Playback Switch' ON numid=46,iface=MIXER,name='Speaker Playback Volume' 63 (max value)
and then the playback start but at speaker output I have only noise no sound!!
I have another question: it's better to use wm8974 like master? because I have see that the atmel ssc is not so good generating clocks....
Thank you
Michele
Best regards
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Fri, Jun 17, 2011 at 05:03:01PM +0200, Michele Da Rold wrote:
it's better to use wm8974 like master? because I have see that the atmel ssc is not so good generating clocks....
Probably in your system, yes. The most important thing from an audio performance point of view is that the clocks are all derived from the same clock source - if this is like other Atmel systems then you need to make the CODEC clock master on the audio bus as the MCLK output from the CPU is not synchronous with the audio clocks.
Hello everybody.
In my previous post I have described my problems, now I'm in this situation:
1. wm8974 configured as slave, sam9g45 master generate a 12Mhz clock con MCLK, the SSC1 generate BCLK and frame signal with right frequency 22khz, 44.1khz etc. (I know that is't better to use wm8974 like master but for now I use this configuration...)
2. I try to play a mp3 (with mpg123) o a video (with mplayer). I see the signal generated by MCU, and the DACDAT signal.
3. I use the default configuration of wm8974 registers, I put the volume to 100%, the result on headphone output is noise, only noise.... Stopping playback the noise stop so I think that I'm playing a sound but no output!!
Someone could help me? I don't know if I'm in the right list.... could you suggest me another one better for my questions?
Best regards Michele
On Tue, Jun 28, 2011 at 12:22:39PM +0200, Michele Da Rold wrote:
- I use the default configuration of wm8974 registers, I put the
volume to 100%, the result on headphone output is noise, only noise.... Stopping playback the noise stop so I think that I'm playing a sound but no output!!
This sounds like your clocking configuration is bad and the CODEC isn't parsing the data sent by the CPU.
Il 29/06/2011 05:03, Mark Brown ha scritto:
On Tue, Jun 28, 2011 at 12:22:39PM +0200, Michele Da Rold wrote:
- I use the default configuration of wm8974 registers, I put the
volume to 100%, the result on headphone output is noise, only noise.... Stopping playback the noise stop so I think that I'm playing a sound but no output!!
This sounds like your clocking configuration is bad and the CODEC isn't parsing the data sent by the CPU.
You suggestion is ok, it was a problem with clock (MCLK) not I can do a playback without problem. and now capture a sound... thank you Michele
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Mark Brown <broonie <at> opensource.wolfsonmicro.com> writes:
On Fri, Jun 17, 2011 at 05:03:01PM +0200, Michele Da Rold wrote:
it's better to use wm8974 like master? because I have see that the atmel ssc is not so good generating clocks....
Probably in your system, yes. The most important thing from an audio performance point of view is that the clocks are all derived from the same clock source - if this is like other Atmel systems then you need to make the CODEC clock master on the audio bus as the MCLK output from the CPU is not synchronous with the audio clocks.
Me too working with WM8974 codec and integrating with AM1808 processor but i while i'm playing i can't hear the sound from speaker.
Configured the WM8974 as slave and connected the MCLK with 24.5MHz crystal. I can probe the signal at WCLK,BCLK and MCLK while playing audio but can't hear the sound from speaker.
My modified machine driver is
#define AUDIO_FORMAT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS )
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; int ret = 0; unsigned sysclk;
/* ASP1 on DM355 EVM is clocked by an external oscillator */ if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || machine_is_davinci_dm365_evm()) sysclk = 27000000;
/* 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. */ if (machine_is_omapl138_lcdkboard()) sysclk = 24576000; else return -EINVAL;
/* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); if (ret < 0) return ret;
/* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT); if (ret < 0) return ret;
ret = snd_soc_dai_set_pll(codec_dai, 0, 1, 24576000,0); if (ret) { printk( ">>>couldn't set pll: %d\n", ret); return ret; } return 0; }
static int evm_spdif_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;
/* set cpu DAI configuration */ return snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT); }
/* * Since da850_sdi_shutdown() and da850_sdi_trigger() are invoked in interrupt * context, they can't directly invoke da850_sdi_mute(), since da850_sdi_mute() * involves i2c communications (which is slow and can wait). So, use the workqueue * mechanism to 'finish' processing. */ #ifdef CONFIG_MACH_DAVINCI_DA850_SDI extern int da850_sdi_mute(int state); static void muteTheOutputs( struct work_struct * work ) { da850_sdi_mute( 1 ); } DECLARE_WORK( muteWorkElement, muteTheOutputs );
static void unmuteTheOutputs( struct work_struct * work ) { da850_sdi_mute( 0 ); } DECLARE_WORK( unmuteWorkElement, unmuteTheOutputs );
static int da850_sdi_trigger(struct snd_pcm_substream *stream, int cmd) { switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: schedule_work( &unmuteWorkElement ); break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: schedule_work( &muteWorkElement ); break; }
return 0; }
static struct snd_soc_ops da850_sdi_ops = { .hw_params = evm_hw_params, .trigger = da850_sdi_trigger }; #endif static struct snd_soc_ops evm_ops = { .hw_params = evm_hw_params, };
static struct snd_soc_ops evm_spdif_ops = { .hw_params = evm_spdif_hw_params, };
static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), }; /* davinci-evm machine audio_mapnections to the codec pins */
static const struct snd_soc_dapm_route audio_map[] = { /* Speaker output mixer */ {"Speaker Mixer", "PCM Playback Switch", "DAC"}, {"Speaker Mixer", "Aux Playback Switch", "Aux Input"}, {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"}, /* speaker connected to SPKOUTP */ {"SpkN Out", NULL, "Speaker Mixer"}, {"SpkP Out", NULL, "Speaker Mixer"}, {"SPKOUTN", NULL, "SpkN Out"}, {"SPKOUTP", NULL, "SpkP Out"}, /* Input PGA */ {"Input PGA", "Aux Switch", "Aux Input"}, {"Input PGA", "MicN Switch", "MICN"}, {"Input PGA", "MicP Switch", "MICP"}, }; /* Logic for a aic3x as connected on a davinci-evm */ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm;
/* Add davinci-evm specific widgets */ snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, ARRAY_SIZE(aic3x_dapm_widgets));
/* Set up davinci-evm specific audio path audio_map */ snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); snd_soc_dapm_enable_pin(dapm, "Ext Spk");
return 0; }
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, };
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 (machine_is_omapl138_lcdkboard()) { printk("LCDK HAWK board"); 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) { platform_device_unregister(evm_snd_device); }
module_init(evm_init); module_exit(evm_exit); root@omapl138-lcdk:~# amixer cset numid=61 1 numid=61,iface=MIXER,name='Speaker Mixer PCM Playback Switch' ; type=BOOLEAN,access=rw------,values=1 : values=on root@omapl138-lcdk:~# amixer cset numid=46,iface=MIXER,name='Speaker Playback Vo lume' 63 numid=46,iface=MIXER,name='Speaker Playback Volume' ; type=INTEGER,access=rw---R--,values=1,min=0,max=63,step=0 : values=63 | dBscale-min=-57.00dB,step=1.00dB,mute=0
Please help me to solve this problem.
participants (3)
-
Mark Brown
-
Michele Da Rold
-
Sangilikumar