[alsa-devel] ASoC: Hooking a TI CODEC to a i.MX27 MCU

Stuart Longland redhatter at gentoo.org
Mon May 24 09:49:46 CEST 2010

Hi all,

I'm trying to write a new machine driver and CODEC driver for a device
we're building, and as yet, haven't figured out the magic that gets ALSA
to enumerate all the devices on the board.  I hope to be able to release
any code here back to the community, but I probably need to clear it
with a few people first (and it'd be nice to contribute _working_ code).

	MCU is a Freescale i.MX27 processor on a Ka-Ro TX27 module
	CODEC is a Texas Instruments TLV320AIC3204, control via I²C,
	data via I²S.  Chip ID for this CODEC is locked at 0x18.
	Kernel is 2.6.34, userland is Gentoo/ARM via root-over-NFS.

	My goal: Just to get audio being transmitted along the I²S bus.
	(Don't care about mixers, etc.)

So far my only means of debugging everything is to sprinkle the code
liberally with printk's everywhere... crude, but it gives me some idea
what's going on.

The CODEC driver is at this moment, an empty shell based on the
TLV320AIC3x driver already in the tree.  I've tried basing a machine
driver on the rx51 driver -- substituting the TLV320AIC3x driver for my
own shell driver, and swapping the original cpu_dai configuration for
the i.MX driver.  I suspect this machine driver is giving me grief.

My machine code setup is as follows:
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link jem3_dai[] = {
                .name = "TLV320AIC3204",
                .stream_name = "AIC3204",
                .cpu_dai = &imx_ssi_pcm_dai[0],
                .codec_dai = &aic3204_dai,
                .init = jem3_aic3204_init,
                .ops = &jem3_ops,

/* Audio private data */
static struct aic3204_setup_data jem3_aic3204_setup = {
        .gpio_func[0] = AIC3204_GPIO1_FUNC_DISABLED,
        .gpio_func[1] = AIC3204_GPIO2_FUNC_DIGITAL_MIC_INPUT,

/* Audio card */
static struct snd_soc_card jem3_sound_card = {
        .name = "JEM3",
        .dai_link = jem3_dai,
        .num_links = ARRAY_SIZE(jem3_dai),
        .platform = &imx_soc_platform,

/* Audio subsystem */
static struct snd_soc_device jem3_snd_devdata = {
        .card = &jem3_sound_card,
        .codec_dev = &soc_codec_dev_aic3204,
        .codec_data = &jem3_aic3204_setup,

static struct platform_device *jem3_snd_device;

static int __init jem3_soc_init(void)
        int err;
        printk( KERN_INFO "%s: hello...\n", __FUNCTION__ );

        jem3_snd_device = platform_device_alloc("soc-audio", -1);
        printk( KERN_INFO "%s: jem3_snd_device = %p\n", 
                        __FUNCTION__, jem3_snd_device );
        if (!jem3_snd_device) {
                err = -ENOMEM;
                goto err1;

        printk( KERN_INFO "%s: calling platform_set_drvdata( %p, %p
                __FUNCTION__, jem3_snd_device, &jem3_snd_devdata );
        platform_set_drvdata(jem3_snd_device, &jem3_snd_devdata);
        jem3_snd_devdata.dev = &jem3_snd_device->dev;

        printk( KERN_INFO "%s: calling platform_device_add(%p)\n",
                __FUNCTION__, jem3_snd_device );
        err = platform_device_add(jem3_snd_device);
        printk( KERN_INFO "%s: platform_device_add(%p) = %d\n",
                __FUNCTION__, jem3_snd_device, err );
        if (err)
                goto err2;

        printk( KERN_INFO "%s = 0 (success)\n", __FUNCTION__ );
        return 0;
        printk( KERN_INFO "%s = %d\n", __FUNCTION__, err );
        return err;

Now this compiles... but when I go to load it; one of two things
happens... either practically nothing (at this stage; no modules are
loaded prior to calling modprobe):

192 / # modprobe snd-soc-jem3
aic3204_i2c_init: adding driver at bf068f9c
aic3204_i2c_init: i2c_add_driver(bf068f9c) = 0
jem3_soc_init: hello...
jem3_soc_init: jem3_snd_device = c3c600a0
jem3_soc_init: calling platform_set_drvdata( c3c600a0, bf07a780 )
jem3_soc_init: calling platform_device_add(c3c600a0)
jem3_soc_init: platform_device_add(c3c600a0) = 0
jem3_soc_init = 0 (success)
192 / # mount /proc
192 / # cat /proc/asound/cards 
--- no soundcards ---

... Or it goes kaboom... particularly if I rmmod the snd-soc-jem3
module, but leave snd-soc-tlv320aic3204 in place...

192 / # rmmod snd-soc-jem3
jem3_soc_exit: unregistering...
jem3_soc_exit: goodbye
192 / # modprobe snd-soc-jem3
jem3_soc_init: hello...
jem3_soc_init: jem3_snd_device = c3c600a0
jem3_soc_init: calling platform_set_drvdata( c3c600a0, bf080780 )
jem3_soc_init: calling platform_device_add(c3c600a0)
Unable to handle kernel NULL pointer dereference at virtual address
pgd = c3e44000
[00000008] *pgd=a3e62031, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1] PREEMPT
last sysfs file: 
Modules linked in: snd_soc_jem3(+) snd_soc_imx snd_soc_tlv320aic3204
snd_soc_core snd_pcm snd_timer snd soundcore snd_page_alloc ac97_bus
unloaded: snd_soc_jem3]
CPU: 0    Not tainted  (2.6.34-jacques-jem3 #16)
PC is at snd_soc_instantiate_cards+0x2c/0x7b4 [snd_soc_core]
LR is at 0x0
pc : [<bf0513d8>]    lr : [<00000000>]    psr: 20000013
sp : c3e33db0  ip : bf05906c  fp : bf059074
r10: 00000001  r9 : bf058fd0  r8 : 00000000
r7 : bf07a790  r6 : c3c600a8  r5 : bf072960  r4 : bf059074
r3 : 00000000  r2 : 00000000  r1 : bf072960  r0 : bf059074
Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 0005317f  Table: a3e44000  DAC: 00000015
Process modprobe (pid: 278, stack limit = 0xc3e32270)
Stack: (0xc3e33db0 to 0xc3e34000)
3da0:                                     c3e33dec 00000001 c3c600a8 00000000
3dc0: bf0687c8 00000000 c034f1c8 00000000 c3e32000 c0167c18 c02eee78 c3e33e34
3de0: c3e33e6c c3c0e7a8 00000000 c0275b24 c3c15040 c003a008 c3c15040 000000d0
3e00: c02e2fbc c3e32000 c3eae9c8 c3e85428 c3e33e34 00000008 c034f1dc c3eafac0
3e20: c3eafac0 c3e85428 c3eae9c8 c00f2674 00000000 0000072d c3e33e60 c3eae9c8
3e40: c3e85428 c00f2724 c0355190 00000000 c3eae9c8 bf080790 bf080798 c3c600a8
3e60: 00000000 c3c600b0 00000000 c033ec88 00000000 bf052ed0 c0355190 c3c600a8
3e80: c3e33eb0 bf059020 c0355190 c01ac47c bf059020 c01ab394 c3c600a8 00000000
3ea0: c3e33eb0 c3c600a8 c01ab548 c01aa628 c3c044e8 c3e76ad4 c02de99c c3c600a8
3ec0: c3c600a8 c3c600dc 00000000 c01ab5f4 c3c600a8 c3c600a8 00000000 c01aa5a8
3ee0: 00000000 c01a8c20 bf080870 c3c600b0 c3c600b0 00000000 00000000 c0168c18
3f00: c3c600a8 c3c600a8 c3c600a0 00000000 bf083000 c0021b88 00000000 c034578c
3f20: 000192dc c01acaa0 c3c600a8 bf0809ac fffffff4 bf080780 bf083000 bf083090
3f40: 000020f1 bf080864 c3e32000 c0021374 00000000 00000000 00000000 000020f1
3f60: bf080864 00021388 000020f1 bf080864 00021388 00000000 c0021b88 c3e32000
3f80: 00000000 c006e48c 00000001 00000000 00019738 0000cf60 000190b0 00019330
3fa0: 00000080 c00219e0 0000cf60 000190b0 00021388 000020f1 00019370 00019370
3fc0: 0000cf60 000190b0 00019330 00000080 00000000 bec39994 00000000 000192dc
3fe0: 000192d0 bec39524 0000bc98 4010ab44 60000010 00021388 00ffff00 00ffff00
[<bf0513d8>] (snd_soc_instantiate_cards+0x2c/0x7b4 [snd_soc_core]) from
[<bf052ed0>] (soc_probe+0x74/0xb0 [snd_soc_core])
[<bf052ed0>] (soc_probe+0x74/0xb0 [snd_soc_core]) from [<c01ac47c>]
[<c01ac47c>] (platform_drv_probe+0x1c/0x24) from [<c01ab394>]
[<c01ab394>] (driver_probe_device+0x88/0x180) from [<c01aa628>]
[<c01aa628>] (bus_for_each_drv+0x60/0x8c) from [<c01ab5f4>]
[<c01ab5f4>] (device_attach+0x5c/0x74) from [<c01aa5a8>]
[<c01aa5a8>] (bus_probe_device+0x30/0x50) from [<c01a8c20>]
[<c01a8c20>] (device_add+0x1f4/0x4c0) from [<c01acaa0>]
[<c01acaa0>] (platform_device_add+0xf0/0x194) from [<bf083090>]
(jem3_soc_init+0x90/0x110 [snd_soc_jem3])
[<bf083090>] (jem3_soc_init+0x90/0x110 [snd_soc_jem3]) from [<c0021374>]
[<c0021374>] (do_one_initcall+0x2c/0x1a8) from [<c006e48c>]
[<c006e48c>] (sys_init_module+0xc4/0x1f8) from [<c00219e0>]
Code: e1530002 0a000021 e597203c e5973010 (e5922008) 
---[ end trace 2b2b9768e2c2da90 ]---

The address mentioned there makes me think there's an uninitialised
pointer to a struct somewhere... but I've never been able to figure out
which one.

If I reboot, and try to make it oops like before by loading the CODEC
driver then machine driver, everything is serine as one would expect:

192 / # modprobe snd-soc-tlv320aic3204
aic3204_i2c_init: adding driver at bf068f9c
aic3204_i2c_init: i2c_add_driver(bf068f9c) = 0
192 / # modprobe snd-soc-jem3
jem3_soc_init: hello...
jem3_soc_init: jem3_snd_device = c3dbb2a0
jem3_soc_init: calling platform_set_drvdata( c3dbb2a0, bf07a780 )
jem3_soc_init: calling platform_device_add(c3dbb2a0)
jem3_soc_init: platform_device_add(c3dbb2a0) = 0
jem3_soc_init = 0 (success)

...but very quiet...
192 / # mount /proc
192 / # cat /proc/asound/cards
--- no soundcards ---

	- I notice in old drivers, the I²C chip address of the CODEC
	  could be passed in via the same means that is used here for
	  GPIO configuration.  How is this done now?  Or how do I tell
	  the kernel to only look at address 0x18?
	- Despite duplicating what I can see being done in other
	  drivers, I still don't see a sound device created.  What am I
	  missing to make an audio device appear?
	- How does one determine what line foo_bar+0x12/0x34 refers to?

As you can tell, I'm a newcomer to kernel hacking, so my appologies if
these have been answered elsewhere... I've spent many days looking and
haven't stumbled upon the answers as yet, hence why I ask here.

Thanks in advance.
Stuart Longland (aka Redhatter, VK4MSL)      .'''.
Gentoo Linux/MIPS Cobalt and Docs Developer  '.'` :
. . . . . . . . . . . . . . . . . . . . . .   .'.'
http://dev.gentoo.org/~redhatter             :.'

I haven't lost my mind...
  ...it's backed up on a tape somewhere.

More information about the Alsa-devel mailing list