[alsa-devel] Need general idea how to architect driver for multi-codec board
Hello All, I'm building a board with multiple TLV320AIC33 codecs on board, and I don't know how I should architect the interface.
The board is set up to use TDM (16 channels on 1 TDM PORT), it has 16 input and 16 output channels, all bonded together -- the hardware does not allow different codecs to run at different speeds.
The board also includes several other widgets, like an I2C mux (to address all 8 stereo codecs), a couple of power amplifiers, and a limited number of ways the audio can be routed in or out.
My question is this: I'm not sure how to architect the driver:
1) Simply instantiate multiple TLV320IAC33 codec drivers, as the AIC33 driver is already written. This seems like it won't work because I'd end up with multiple user-space 'cards', yet I want the card to appear as a single 16-channel sound card. Also, when you start one PCM interface for one codec, they would all start automatically, which I imagine the linux system wouldn't get. Also, the PCM interface needs to be configured differently for each codec (the TDM offset must be 0, 16, 32, 48, etc. bits).
2) rewrite the AIC33 driver to include all 8 codecs, including all the mixer controls for each codec. This is going to get REALLY big and ugly in the user space mixer control -- there will be a zillion controls, most of which are not really useful for this board.
3) rewrite the AIC33 driver to include all 8 codecs, but pare down the mixer controls to just the ones that I care about. If the card changes later, add in the changes to the driver.
I don't have any huge desire to get the driver into the mainstream kernel (though I would not be upset if it was popular enough to warrant that), it's just not a priority.
Any thoughts would be most welcome.
Thanks, -Caleb
On Wed, May 11, 2011 at 10:31:15AM -0700, Caleb Crome wrote:
- Simply instantiate multiple TLV320IAC33 codec drivers, as the AIC33
driver is already written.
Yes.
This seems like it won't work because I'd end up with multiple
user-space 'cards', yet I want the card to appear as a single 16-channel sound card. Also, when you start one PCM interface for one codec, they
No, you shouldn't. You'll end up with a single card.
would all start automatically, which I imagine the linux system wouldn't get. Also, the PCM interface needs to be configured differently for each codec (the TDM offset must be 0, 16, 32, 48, etc. bits).
The TDM stuff should be configured with the existing TDM API (you may need to implement this in the CODEC driver).
Well, that does sound easier than rewriting the whole thing. I'll give it a go at instantiating multiple codecs.
Would this be 'substreams' or is that something else?
Sorry to be ignorant, but I'm a newbie to kernel development (I've successfully brought up GPIOs and the I2C mux on my board, so I at least have some simple stuff under my belt).
Thanks again, -Caleb
On Wed, May 11, 2011 at 10:50 AM, Mark Brown < broonie@opensource.wolfsonmicro.com> wrote:
On Wed, May 11, 2011 at 10:31:15AM -0700, Caleb Crome wrote:
- Simply instantiate multiple TLV320IAC33 codec drivers, as the AIC33
driver is already written.
Yes.
This seems like it won't work because I'd end up with multiple
user-space 'cards', yet I want the card to appear as a single 16-channel sound card. Also, when you start one PCM interface for one codec, they
No, you shouldn't. You'll end up with a single card.
would all start automatically, which I imagine the linux system wouldn't get. Also, the PCM interface needs to be configured differently for each codec (the TDM offset must be 0, 16, 32, 48, etc. bits).
The TDM stuff should be configured with the existing TDM API (you may need to implement this in the CODEC driver).
Hi again, I've been poking through the soc-core.c and other soc files, and I'm still at a bit of a loss how to add 8 cocdecs on 3 different I2C (virtual) busses onto one card.
Do I set up my 'card' with multiple codecs as multiple dai links?
static struct snd_soc_dai_link puppy_dai[] = { { .name = "puppy", .stream_name = "puppy", .cpu_dai_name = "omap-mcbsp-dai.0", .platform_name = "omap-pcm-audio", .codec_dai_name = "tlv320aic3x-se-hifi", .codec_name = "tlv320aic3x-se-codec.4-0018", .ops = &puppy_ops, }, { .name = "puppy", .stream_name = "puppy", .cpu_dai_name = "omap-mcbsp-dai.0", .platform_name = "omap-pcm-audio", .codec_dai_name = "tlv320aic3x-se-hifi", .codec_name = "tlv320aic3x-se-codec.4-0019", .ops = &puppy_ops, }, ... 6 more codecs ... }
static struct snd_soc_card snd_soc_puppy = { .name = "tlv320aic33-se", .owner = THIS_MODULE, .dai_link = puppy_dai, .num_links = ARRAY_SIZE(puppy_dai),
};
I'm afraid I don't understand how it's supposed to work. How do I get the sound card to understand the multiple codecs? Is the above the right direction, or am I way off?
All the examples of multiple links on one card all seemed to be exlcusive of one another, and ran over different cpu DAIs. I need these all to run on the same CPU DAI but TDMd.
Is each codec supposed to have its own substream, or do they all get aggregated into one 16-channel substream?
Thanks again, -Caleb
On Wed, May 11, 2011 at 11:29 AM, Caleb Crome caleb@crome.org wrote:
Well, that does sound easier than rewriting the whole thing. I'll give it a go at instantiating multiple codecs.
Would this be 'substreams' or is that something else?
Sorry to be ignorant, but I'm a newbie to kernel development (I've successfully brought up GPIOs and the I2C mux on my board, so I at least have some simple stuff under my belt).
Thanks again, -Caleb
On Wed, May 11, 2011 at 10:50 AM, Mark Brown < broonie@opensource.wolfsonmicro.com> wrote:
On Wed, May 11, 2011 at 10:31:15AM -0700, Caleb Crome wrote:
- Simply instantiate multiple TLV320IAC33 codec drivers, as the AIC33
driver is already written.
Yes.
This seems like it won't work because I'd end up with multiple
user-space 'cards', yet I want the card to appear as a single 16-channel sound card. Also, when you start one PCM interface for one codec, they
No, you shouldn't. You'll end up with a single card.
would all start automatically, which I imagine the linux system wouldn't get. Also, the PCM interface needs to be configured differently for
each
codec (the TDM offset must be 0, 16, 32, 48, etc. bits).
The TDM stuff should be configured with the existing TDM API (you may need to implement this in the CODEC driver).
Hi Mark, You previously indicated that I can instantiate multiple tlv320aic33 drivers. Can you please give me a clue how to do that? I've been staring at this for days, and I still don't really get how it's supposed to be done.
I tried adding the I2C devices, and it does get to the probe functions. However, when it comes to adding the mixer controls (SND_SOC_XXXX), it fails because there are already controls with those names, like "Right PGA Mixer" etc. The alsa layer doesn't seem to allow multiple registrations of the same control. How do I give each instantiation a unique identifier? Since the tlv320aic33 has about 80 controls, I'm going to end up with over 600 controls in alsamixer.
Are there any examples in the world that show multiple codecs in the same sound card that I can get a clue from?
Again, any help would be most welcome.
Thanks, -Caleb
On Wed, May 11, 2011 at 10:50 AM, Mark Brown < broonie@opensource.wolfsonmicro.com> wrote:
On Wed, May 11, 2011 at 10:31:15AM -0700, Caleb Crome wrote:
- Simply instantiate multiple TLV320IAC33 codec drivers, as the AIC33
driver is already written.
Yes.
This seems like it won't work because I'd end up with multiple
user-space 'cards', yet I want the card to appear as a single 16-channel sound card. Also, when you start one PCM interface for one codec, they
No, you shouldn't. You'll end up with a single card.
would all start automatically, which I imagine the linux system wouldn't get. Also, the PCM interface needs to be configured differently for each codec (the TDM offset must be 0, 16, 32, 48, etc. bits).
The TDM stuff should be configured with the existing TDM API (you may need to implement this in the CODEC driver).
On Thu, May 26, 2011 at 02:23:03PM -0700, Caleb Crome wrote:
You previously indicated that I can instantiate multiple tlv320aic33 drivers. Can you please give me a clue how to do that? I've been staring at this for days, and I still don't really get how it's supposed to be done.
With things like this it's always useful to look for other examples of whatever it is you're doing - there's systems like speyside and rx51 in mainline.
fails because there are already controls with those names, like "Right PGA Mixer" etc. The alsa layer doesn't seem to allow multiple registrations of the same control. How do I give each instantiation a unique identifier?
Set up a snd_soc_codec_conf for each CODEC and assign a name_prefix to it.
Since the tlv320aic33 has about 80 controls, I'm going to end up with over 600 controls in alsamixer.
Yes.
Mark, Thanks, for the pointers. I now have a gazillion controls in my alsamixer :-) The snd_soc_codec_conf works like a charm.
However, I'm still having problems getting the system set up with more than 2 channels. the rx51 and speyside don't quite do the same thing as far as I can tell: the rx51 is close -- it very nearly brings up the 'b' codec in the aic34, but I don't see how the TDM is set -- it appears to still be a stereo configuration.
Do I need multiple snd_soc_dai_links when configuring? In hw_params, I only seem to get 1 codec dai_link, so I can't set the TDM slots there.
Or are the extra codecs in the daisy chain considered 'aux' devices? I guess I could set up the tdm slots in each of the snd_soc_aux_dev.init functions. But that callback init function has a snd_soc_dapm_context a parameter, which doesn't seem to be the right place for setting tdm slots and pin directions.
My 'machine_hw_params' function allows for 1 through 16 channels, and my machine_startup function calls snd_pcm_hw_contraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 1, 16) to allow for 16 channels. But still the only way I can get more than 2 channels from any user program (using jackd), is to change the tlv320aic3x.c driver to set channels_max to 16, which I'm pretty sure is not right.
It seems like I might need to do something like this in the hw_params function:
int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; for (codec_dai = 0; codec_dai < 8; codec_dai++) { snd_soc_dai_set_tdm_slot(codec_dai_list[codec_dai], 0x3<<codec_dai*2, 0x3<<codec_dai*2, 2, 16); snd_soc_dai_set_channel_map(codec_dai_list[codec_dai], 2, &(channel_map[codec_dai*2]), 2, &(channel_map[codec_dai*2])); }
but, I don't see any place where I have access to all 8 codec_dai's.
Any thoughts, suggestions?
Thanks again, -Caleb
On Thu, May 26, 2011 at 6:27 PM, Mark Brown < broonie@opensource.wolfsonmicro.com> wrote:
On Thu, May 26, 2011 at 02:23:03PM -0700, Caleb Crome wrote:
You previously indicated that I can instantiate multiple tlv320aic33 drivers. Can you please give me a clue how to do that? I've been
staring
at this for days, and I still don't really get how it's supposed to be
done.
With things like this it's always useful to look for other examples of whatever it is you're doing - there's systems like speyside and rx51 in mainline.
fails because there are already controls with those names, like "Right PGA
Mixer" etc. The alsa layer doesn't seem to allow multiple registrations
of
the same control. How do I give each instantiation a unique identifier?
Set up a snd_soc_codec_conf for each CODEC and assign a name_prefix to it.
Since the tlv320aic33 has about 80 controls, I'm going to end up with
over
600 controls in alsamixer.
Yes.
On Fri, May 27, 2011 at 02:36:58PM -0700, Caleb Crome wrote:
However, I'm still having problems getting the system set up with more than 2 channels. the rx51 and speyside don't quite do the same thing as far as I can tell: the rx51 is close -- it very nearly brings up the 'b' codec in the aic34, but I don't see how the TDM is set -- it appears to still be a stereo configuration.
There's an API for configuring TDM - snd_soc_dai_set_tdm slot.
Do I need multiple snd_soc_dai_links when configuring? In hw_params, I only
Yes, you will. Or invent a new interface. The current APIs provide point to point links between interfaces. If you're trying to do something different then you'll need to extend the core to support what you need.
seem to get 1 codec dai_link, so I can't set the TDM slots there.
What makes you say that?
Or are the extra codecs in the daisy chain considered 'aux' devices? I
No. aux devices have no digital links. In addition to looking at the existing code you'll also find a lot of background information if you look through the revision control history...
Hi Again, Sorry to keep on this, but I'm really wedged. I can't tell if I'm trying to break new ground, or if I just don't know what I'm doing :-) Or both perhaps.
I've got my multi-codec card instantiating (only 4 codecs here for simplicity -- there are really 8 total)
# insmod tlv320aic3x_se.ko # <--- my custom version of aic3x driver. # insmod puppy_machine.ko # <-- my custom machinne interface with all my dai-links and all.
# aplay -l **** List of PLAYBACK Hardware Devices **** card 0: tlv320aic33se [tlv320aic33-se], device 0: tlv320aic33-se tlv320aic3x-se-hifi-0 [] Subdevices: 0/1 Subdevice #0: subdevice #0 card 0: tlv320aic33se [tlv320aic33-se], device 1: tlv320aic33-se tlv320aic3x-se-hifi-1 [] Subdevices: 1/1 Subdevice #0: subdevice #0 card 0: tlv320aic33se [tlv320aic33-se], device 2: tlv320aic33-se tlv320aic3x-se-hifi-2 [] Subdevices: 1/1 Subdevice #0: subdevice #0 card 0: tlv320aic33se [tlv320aic33-se], device 3: tlv320aic33-se tlv320aic3x-se-hifi-3 [] Subdevices: 1/1 Subdevice #0: subdevice #0
I can run jack on the first subdevice and it actually streams data both ways on codec 0.
# jackd -d alsa -d hw:0,0 creating alsa driver ... hw:0,0|hw:0,0|1024|2|48000|0|0|nomon|swmeter|-|32bit control device hw:0 ...
However, I can't seem to get access to any of the other codecs on the TDM bus. Remember, there is only a single CPU DAI here (a single McBSP port), but multiple codec DAIs (one for each codec, in TDM mode).
# jackd -d alsa -d hw:0,0 -i4 -o4 # <--- Does not work. hw:0,0 is just a single stereo codec, so it doesn't start up the other DAIs
How do I get jackd (or anything else for that matter), to start all the DAIs at the same time?
If I try: # aplay -D hw:0,0 -f S16_LE test.wav # <--- success
and in another shell: # aplay -D hw:0,1 -f S16_LE test1.wav # <--- failure
the second invocation on hw:0,1 fails because hw:0 is already open.
I also tried modifying my asoundrc to something similar to http://quicktoots.linuxaudio.org/toots/el-cheapo, but that didn't work either.
I can attach my code and even schematic if that would help.
Thanks again, -Caleb
On Wed, Jun 01, 2011 at 03:17:10PM -0700, Caleb Crome wrote:
Sorry to keep on this, but I'm really wedged. I can't tell if I'm trying to break new ground, or if I just don't know what I'm doing :-) Or both perhaps.
Like I said in my last mail you're probably trying to break new ground here. You're trying to bind all the CODECs together and treat them as one digital interface, currently the code does a series of point to point links multiplexed on a bus only.
I've got my multi-codec card instantiating (only 4 codecs here for simplicity -- there are really 8 total)
# insmod tlv320aic3x_se.ko # <--- my custom version of aic3x driver.
That's not a good sign... If you've modified the driver you probably want to look into submitting your modifications back upstream.
Okay, I finally got my system going -- in a *very* kludgy way. I re-write tlv320aic3x.c to include all the codecs. Its nasty and ugly and horrible, but it works with no changes to the kernel soc architecture.
Thanks for your help Mark!
I'll start another thread for proposed changes to the core so that this can be done properly.
Thanks again, -Caleb
participants (2)
-
Caleb Crome
-
Mark Brown