[alsa-devel] ASoC and a codec that can't be controlled
I'm working on some ASoC drivers for a new board, and we're using a CS4270. The CS4270 is interesting in that if it is connected in stand-alone mode, there is no way to configure it. The board wirings determine all the parameters.
Therefore, my CS4270 codec driver will probably be very skimpy.
My question is: does this mean that my cs4270.c file will *never* call these functions:
snd_ctl_add snd_soc_cnew snd_soc_dapm_new_control snd_soc_dapm_connect_input snd_soc_dapm_new_widgets
These are the functions used to add new controls and widgets.
So how does ALSA know that it needs to call my I2S driver *instead* of my codec driver to do stuff like change volume?
On Tue, 2007-05-22 at 10:47 -0500, Timur Tabi wrote:
I'm working on some ASoC drivers for a new board, and we're using a CS4270. The CS4270 is interesting in that if it is connected in stand-alone mode, there is no way to configure it. The board wirings determine all the parameters.
Therefore, my CS4270 codec driver will probably be very skimpy.
My question is: does this mean that my cs4270.c file will *never* call these functions:
snd_ctl_add snd_soc_cnew
These functions are only required in the codec driver to change volume, mixer settings etc. If your codec has no volume or mixers then you don't need them.
snd_soc_dapm_new_control snd_soc_dapm_connect_input snd_soc_dapm_new_widgets
These are used for setting up the dynamic audio power management and won't be needed in stand alone mode.
These are the functions used to add new controls and widgets.
So how does ALSA know that it needs to call my I2S driver *instead* of my codec driver to do stuff like change volume?
You probably want to create a volume kcontrol in your I2S driver. I assume your I2S controller can digitally adjust the volume by altering the PCM stream ? The kcontrols can be added in your I2S driver probe function.
Liam
Liam Girdwood wrote:
These are used for setting up the dynamic audio power management and won't be needed in stand alone mode.
So if I don't care about power management, can I completely ignore anything with "dapm" in it?
Also, do I need a codec driver at all, even if I can't control the codec? That is, can I do stuff like this:
static struct snd_soc_device mysoc_snd_devdata = { .machine = &snd_soc_machine_mysoc, .platform = &mysoc_soc_platform, };
static struct snd_soc_dai_link mpc8610hpcd_dai = { .name = "CS4270", .stream_name = "CS4270", .cpu_dai = &mysoc_i2s_dai, .init = mpc8610hpcd_machine_init, .ops = &mpc8610hpcd_ops, };
?
On Fri, 2007-05-25 at 15:17 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
These are used for setting up the dynamic audio power management and won't be needed in stand alone mode.
So if I don't care about power management, can I completely ignore anything with "dapm" in it?
Yes.
Also, do I need a codec driver at all, even if I can't control the codec? That is, can I do stuff like this:
static struct snd_soc_device mysoc_snd_devdata = { .machine = &snd_soc_machine_mysoc, .platform = &mysoc_soc_platform, };
static struct snd_soc_dai_link mpc8610hpcd_dai = { .name = "CS4270", .stream_name = "CS4270", .cpu_dai = &mysoc_i2s_dai, .init = mpc8610hpcd_machine_init, .ops = &mpc8610hpcd_ops, };
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
e.g. supported sample rates, interface formats, etc
Liam
Liam Girdwood wrote:
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
e.g. supported sample rates, interface formats, etc
I'm curious - why would the codec driver dictate all that? On my board, the sample rates are determined by the I2S controller, not the codec, because in order to change the sample rate, I program new divisors into the I2S controller's registers.
On Mon, 2007-05-28 at 19:18 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
e.g. supported sample rates, interface formats, etc
I'm curious - why would the codec driver dictate all that? On my board, the sample rates are determined by the I2S controller, not the codec, because in order to change the sample rate, I program new divisors into the I2S controller's registers.
Some codecs can only support a small range of sample rates e.g. 8k & 48k, whilst your controller may support more. This just makes sure the audio is set a rate the codec can handle, otherwise audio quality will probably suffer.
Liam
Liam Girdwood wrote:
Some codecs can only support a small range of sample rates e.g. 8k & 48k, whilst your controller may support more. This just makes sure the audio is set a rate the codec can handle, otherwise audio quality will probably suffer.
What about boards where the codec does *not* dictate the available sample rates? That is, what if the SOC can't generate the frequencies necessary to drive the codec at all the rates it supports? The codec driver would then need to be told by the machine driver which sample rates are going to be used. When the codec driver is initialized, it needs to set the snd_soc_codec.dai field to point to the codec's DAI, and that structure contains the list of supported sample rates.
I guess in the PowerPC world, the codec driver can query the codec node in the device tree, but ideally there would need to be some standard interface.
On Tue, 2007-05-29 at 13:10 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
Some codecs can only support a small range of sample rates e.g. 8k & 48k, whilst your controller may support more. This just makes sure the audio is set a rate the codec can handle, otherwise audio quality will probably suffer.
What about boards where the codec does *not* dictate the available sample rates? That is, what if the SOC can't generate the frequencies necessary to drive the codec at all the rates it supports? The codec driver would then need to be told by the machine driver which sample rates are going to be used. When the codec driver is initialized, it needs to set the snd_soc_codec.dai field to point to the codec's DAI, and that structure contains the list of supported sample rates.
ASoC creates a bitmask of supported sample rates from the DMA, I2S controller and codec and then AND's them together. It then checks the requested rate, etc against this bitmask before proceeding. This way the whole audio system dictates the supported rate and not any single component.
Liam
Liam Girdwood wrote:
On Fri, 2007-05-25 at 15:17 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
These are used for setting up the dynamic audio power management and won't be needed in stand alone mode.
So if I don't care about power management, can I completely ignore anything with "dapm" in it?
Yes.
Also, do I need a codec driver at all, even if I can't control the codec? That is, can I do stuff like this:
static struct snd_soc_device mysoc_snd_devdata = { .machine = &snd_soc_machine_mysoc, .platform = &mysoc_soc_platform, };
static struct snd_soc_dai_link mpc8610hpcd_dai = { .name = "CS4270", .stream_name = "CS4270", .cpu_dai = &mysoc_i2s_dai, .init = mpc8610hpcd_machine_init, .ops = &mpc8610hpcd_ops, };
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
Another question:
Why do I need to specify the codec DAI in two different structures?
eti_b1_wm8731.c:
static struct snd_soc_dai_link eti_b1_dai = { .name = "WM8731", .stream_name = "WM8731", .cpu_dai = &at91_i2s_dai[1], .codec_dai = &wm8731_dai, <---- .init = eti_b1_wm8731_init, .ops = &eti_b1_ops, };
wm8731.c:
static int wm8731_init(struct snd_soc_device *socdev) { struct snd_soc_codec *codec = socdev->codec; ... int reg, ret = 0;
codec->dai = &wm8731_dai; <-----
On Tue, 2007-05-29 at 13:47 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
On Fri, 2007-05-25 at 15:17 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
These are used for setting up the dynamic audio power management and won't be needed in stand alone mode.
So if I don't care about power management, can I completely ignore anything with "dapm" in it?
Yes.
Also, do I need a codec driver at all, even if I can't control the codec? That is, can I do stuff like this:
static struct snd_soc_device mysoc_snd_devdata = { .machine = &snd_soc_machine_mysoc, .platform = &mysoc_soc_platform, };
static struct snd_soc_dai_link mpc8610hpcd_dai = { .name = "CS4270", .stream_name = "CS4270", .cpu_dai = &mysoc_i2s_dai, .init = mpc8610hpcd_machine_init, .ops = &mpc8610hpcd_ops, };
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
Another question:
Why do I need to specify the codec DAI in two different structures?
eti_b1_wm8731.c:
static struct snd_soc_dai_link eti_b1_dai = { .name = "WM8731", .stream_name = "WM8731", .cpu_dai = &at91_i2s_dai[1], .codec_dai = &wm8731_dai, <---- .init = eti_b1_wm8731_init, .ops = &eti_b1_ops, };
wm8731.c:
static int wm8731_init(struct snd_soc_device *socdev) { struct snd_soc_codec *codec = socdev->codec; ... int reg, ret = 0;
codec->dai = &wm8731_dai; <-----
Looks like some naming confusion on my part here, so his probably needs a little refactoring in the codec struct.
The codec struct contains a pointer to _all_ the digital audio interfaces it supports (e.g. some codecs support more than 1 DAI), whilst the link struct contains a mapping between codec interface n and SoC interface n. In this example the WM8731 only has 1 DAI, so it does look like duplication.
I'll refactor this in a future patch.
Liam
Liam Girdwood wrote:
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
Another question:
Why is it the codec driver that calls snd_soc_new_pcms()? Shouldn't the PCM driver be doing that?
Looking in snd_soc_new_pcms() itself, I see this:
codec->card = snd_card_new(idx, xid, codec->owner, 0);
Here we create a new "sound card", and we assign it to the
I guess I just don't understand why the codec driver is acting like the "master" driver of ASOC. IMHO, the codec driver should be doing two things:
1) Specifying the capabilities of the codec hardware itself, without any assumption that these capabilities dictate the capabilities of the system as a whole (e.g. the codec shouldn't assume that the system supports every sampling rate that the codec does). 2) Providing APIs for controlling the codec.
ASOC and the machine driver should then work in tandem to decide which driver will do what and which capabilities are *actually* supported. *Something* needs to look at the entire system and say to each device, "Well, yes, I know about this little feature of yours, but we're just not going to support that today."
Liam Girdwood wrote:
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
Ok, I just noticed this:
at91-i2s.c:
#define AT91_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000)
struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { { .name = "at91_ssc0/i2s", .id = 0, .type = SND_SOC_DAI_I2S, .suspend = at91_i2s_suspend, .resume = at91_i2s_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = AT91_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = AT91_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,},
wm8731.c:
#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000)
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE)
struct snd_soc_codec_dai wm8731_dai = { .name = "WM8731", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,},
So does this mean that ALSA looks at the rate and format capabilities of the I2S interface and the codec, and then only chooses ones that both support?
Also, what does it mean for the codec to support little-endian? On PowerPC, all registers are big-endian, so my version of at91-i2s.c has to have SNDRV_PCM_FMTBIT_S24_BE.
Your codec driver says it's little-endian. But the I2S standard is serial, so endianness doesn't apply. Yet if I say the codec is little-endian, but the I2S interface is big-endian, how can ALSA resolve that?
On Tue, 2007-05-29 at 18:05 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
Yes, although my feeling is that a codec "driver" would still be needed to define the capabilities of your codec within the audio subsystem.
Ok, I just noticed this:
at91-i2s.c:
#define AT91_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000)
struct snd_soc_cpu_dai at91_i2s_dai[NUM_SSC_DEVICES] = { { .name = "at91_ssc0/i2s", .id = 0, .type = SND_SOC_DAI_I2S, .suspend = at91_i2s_suspend, .resume = at91_i2s_resume, .playback = { .channels_min = 1, .channels_max = 2, .rates = AT91_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { .channels_min = 1, .channels_max = 2, .rates = AT91_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,},
wm8731.c:
#define WM8731_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\ SNDRV_PCM_RATE_96000)
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE)
struct snd_soc_codec_dai wm8731_dai = { .name = "WM8731", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8731_RATES, .formats = WM8731_FORMATS,},
So does this mean that ALSA looks at the rate and format capabilities of the I2S interface and the codec, and then only chooses ones that both support?
Yes.
Also, what does it mean for the codec to support little-endian? On PowerPC, all registers are big-endian, so my version of at91-i2s.c has to have SNDRV_PCM_FMTBIT_S24_BE.
Your codec driver says it's little-endian. But the I2S standard is serial, so endianness doesn't apply. Yet if I say the codec is little-endian, but the I2S interface is big-endian, how can ALSA resolve that?
This was added as some controllers and codecs can shift out data in different formats (e.g. LSB or MSB first). It's only really used atm for data size (e.g. 16, 24 bits) as the endianess is currently not dealt with correctly. I've logged bug :-
https://bugtrack.alsa-project.org/alsa-bug/view.php?id=3132
Fwiw, you should be ok in the mean time as most audio data is stored on file in little endian format. Your media player should open such little endian files as *_LE when it configures the ALSA pcm. This should work if your I2S, DMA and codec are marked as supporting LE formats.
Liam
Liam Girdwood wrote:
Fwiw, you should be ok in the mean time as most audio data is stored on file in little endian format. Your media player should open such little endian files as *_LE when it configures the ALSA pcm. This should work if your I2S, DMA and codec are marked as supporting LE formats.
What about this snippet in asound.h:
#ifdef __LITTLE_ENDIAN #define SNDRV_LITTLE_ENDIAN #else #ifdef __BIG_ENDIAN #define SNDRV_BIG_ENDIAN
...
#ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE #define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_LE #define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_LE ... #endif #ifdef SNDRV_BIG_ENDIAN #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE #define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_BE #define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_BE ... #endif
I could then do this:
#define CS4270_FORMATS SNDRV_PCM_FMTBIT_S24
On big-endian platforms like mine, CS4270_FORMATS will be SNDRV_PCM_FORMAT_S24_BE and on little-endian platforms it will be SNDRV_PCM_FORMAT_S24_LE. Assuming the I2S registers are the same endian as the platform, will this work?
If not, then what about this:
#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
That would tell ALSA that the CS4270 supports both formats, which isn't technically true, but it wouldn't matter because the I2S interface is what determines the actual "endianness" of the serial data.
On Wed, 2007-05-30 at 10:46 -0500, Timur Tabi wrote:
Liam Girdwood wrote:
Fwiw, you should be ok in the mean time as most audio data is stored on file in little endian format. Your media player should open such little endian files as *_LE when it configures the ALSA pcm. This should work if your I2S, DMA and codec are marked as supporting LE formats.
What about this snippet in asound.h:
#ifdef __LITTLE_ENDIAN #define SNDRV_LITTLE_ENDIAN #else #ifdef __BIG_ENDIAN #define SNDRV_BIG_ENDIAN
...
#ifdef SNDRV_LITTLE_ENDIAN #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_LE #define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_LE #define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_LE ... #endif #ifdef SNDRV_BIG_ENDIAN #define SNDRV_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16_BE #define SNDRV_PCM_FORMAT_U16 SNDRV_PCM_FORMAT_U16_BE #define SNDRV_PCM_FORMAT_S24 SNDRV_PCM_FORMAT_S24_BE ... #endif
I could then do this:
#define CS4270_FORMATS SNDRV_PCM_FMTBIT_S24
I'll fix this in the core so we don't need to do this. Please use _LE atm.
On big-endian platforms like mine, CS4270_FORMATS will be SNDRV_PCM_FORMAT_S24_BE and on little-endian platforms it will be SNDRV_PCM_FORMAT_S24_LE. Assuming the I2S registers are the same endian as the platform, will this work?
I think this probably depends on how your audio FIFO's align the data received from memory/DMA. They should do any bit reordering based on the audio format, and size etc set in the control register. (Although, ymmv.)
If not, then what about this:
#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
That would tell ALSA that the CS4270 supports both formats, which isn't technically true, but it wouldn't matter because the I2S interface is what determines the actual "endianness" of the serial data.
This looks fine as a workaround atm, I'll try and have a look at this bug tomorrow so we don't need to do this.
Liam
Liam Girdwood wrote:
On big-endian platforms like mine, CS4270_FORMATS will be SNDRV_PCM_FORMAT_S24_BE and on little-endian platforms it will be SNDRV_PCM_FORMAT_S24_LE. Assuming the I2S registers are the same endian as the platform, will this work?
I think this probably depends on how your audio FIFO's align the data received from memory/DMA. They should do any bit reordering based on the audio format, and size etc set in the control register. (Although, ymmv.)
Ok, I'm a little confused. I was going to specify the SNDRV_PCM_FORMAT_S24_BE for my I2C device because the actual 32-bit memory-mapped registers are big-endian. The structure definition even uses __be32 for each field.
My goal was to specify a single value (i.e. SNDRV_PCM_FORMAT_S24_BE) such that ALSA would give me a 32-bit quantity that exactly matches what my device expects.
I wish asound.h had more documentation. I don't know what any of these macros *really* mean.
#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
That would tell ALSA that the CS4270 supports both formats, which isn't technically true, but it wouldn't matter because the I2S interface is what determines the actual "endianness" of the serial data.
This looks fine as a workaround atm, I'll try and have a look at this bug tomorrow so we don't need to do this.
Ok, I'll do this for now.
participants (2)
-
Liam Girdwood
-
Timur Tabi