After getting many of the PXA16x peripherals working in modern Linux kernels during my Chumby 8 kernel upgrade saga (here are links to parts 1, 2, 3, 4, 5, 6, and 7), I really felt like I was starting to reach the finish line. The display was working well enough to play low-resolution videos, I had basic 2D acceleration up and running, the touchscreen was operational, and Wi-Fi worked flawlessly. Audio was the only major component left to tackle. The Chumby 8 has built-in speakers, a headphone jack, and a microphone.

The Linux sound subsystem is something I had exactly zero experience with prior to this project, so it felt very intimidating. It took me a while to get familiar with it. The relevant project is Advanced Linux Sound Architecture (ALSA) and in particular, the ALSA System on Chip (ASoC) layer is where most of my work would be for this project.

Before digging in too deep, I needed to look at the Chumby 8 schematics and figure out what I would be dealing with. With no prior audio experience to lean on, I wasn’t sure what to expect. Let’s take a look at the relevant section of the schematic:

Okay, so it’s using a Wolfson WM8961 audio codec. This chip was discontinued in 2013. I’m going to be honest — this “codec” terminology confused me at first. I thought an audio codec was more like a data format, such as MP3, AAC, Vorbis, etc. It turns out it’s one of those situations where hardware and software people use the same word with different meanings. My understanding was correct for the software side, but on the hardware side, the word codec is used to refer to an ADC/DAC for converting between analog and digital signals.

The way the WM8961 works is you write a bunch of register values over I2C (the SCLK and SDIN pins) to set it up, and then send/receive audio data to/from it using I2S. Despite the names being similar, I2S is not related to I2C. After playing with it a bit, I actually think it’s pretty similar to SPI. There are a bit clock and data in and out pins just like SPI. The main difference is SPI has a chip select signal to specify when the chip should be paying attention, whereas I2S has an LRCLK signal that indicates which channel (left or right) the data corresponds with.

As you can see above there are SPK_LP/LN and SPK_RP/RN outputs for the speakers. These end up going to a connector on the PCB where the speakers plug in. There’s additionally a bunch of filtering circuitry that I definitely don’t have enough experience to understand. The microphone is also hooked up to this chip (LINPUT). There is also an unused right input channel.

The headphone output is also provided by the WM8961 (HP_L and HP_R). The headphone jack has a detection signal which is used to activate a transistor to control a signal (HP_IN) which will let me know when headphones are plugged in. HP_IN goes straight to one of the PXA166’s GPIO pins.

Here is where the I2C bus goes. The schematic calls it PWR_SCL/SDA, but it’s also known as twsi2 in Linux’s pxa168.dtsi file.

As for the I2S signals, they go into the PXA166’s SSP1 peripheral:

There is also an extra I2S_CDCLK0 signal that goes to the MCLK input of the WM8961. This is known as the “master clock” and is a separate clock that is needed by the codec chip. Typically, based on my understanding, it’s supposed to be a multiple of the sample rate you’re working with. I opted to make it 256 times the sample rate (256Fs). So for example, if I’m playing a 44.1 kHz sound, MCLK will be 11.2896 MHz. It was fairly difficult for me to glean this requirement based on reading the datasheets, but after looking at random discussions online and other sample code it became clearer.

All right, so that gave me enough information to get started. I needed to configure the codec chip over I2C on TWSI2, set up I2S audio on SSP1, output MCLK on AC97_SYSCLK, and detect headphone insertion on GPIO_97. How in the world would I get started?

By looking at the old Chumby 2.6.28 kernel, of course. I looked at Chumby’s initialization scripts and got an idea of what the dependencies would be for playing a sound. Being able to play a sound in 2.6.28 would be a good first step, and it would give me a reference to compare against if I ran into any issues with the newer kernel. I already had a minimal rootfs set up from when I was getting Wi-Fi working, so I just had to copy some more binaries, libraries, config files, etc. out of the original Chumby rootfs into my minimal rootfs for 2.6.28. One thing that was helpful was to use strace to see what was happening when I tried to run commands like amixer and aplay. Eventually after I got enough of the underlying libraries and config files figured out, I came up with this somewhat hacky sequence of commands I could run to play a sound in the old Chumby kernel:

modprobe i2c-pxa
modprobe snd-soc-wm8961
modprobe snd-soc-silvermoon
mdev -s
mkdir /dev/snd
cp -R /dev/controlC0 /dev/pcmC0D0* /dev/timer /dev/snd/
amixer set Speaker 127
aplay /smw_game_over.wav

This played the Super Mario World game over sound for me (here’s where I got it). Yay, I had a simple working test setup in the old kernel. Next up, I looked at how everything was hooked up in that older version. Most of the fun was in the snd/soc/pxa directory — in particular, I saw that snd-soc-pxa3xx, snd-soc-pxa3xx-ssp, and snd-soc-silvermoon were modules being built. silvermoon_wm8961.c seemed to contain the glue to hook everything together. snd/soc/codecs/wm8961.c had some Chumby-specific hacks. I also discovered, unsurprisingly, that the PXA3xx sound driver mostly just seemed like a bunch of DMA (direct memory access) setup stuff. This makes a lot of sense. Once you have everything up and running, you’re mainly just continually using DMA to keep the data flowing to the codec.

I tried to find modern equivalents to these drivers in the mainline kernel. The WM8961 driver still existed, but didn’t support device tree. The sound/soc/pxa directory was also still there, but there was nothing specific to the PXA3xx. Instead, I saw some PXA2xx drivers. It still seemed like these would be helpful. I also found some older, unmerged patches adding PXA168 support from 2010. I figured I could look at these and try to fit them in with the newer kernel.

Here’s an overview of what I needed to accomplish:

  • Enable I2C support for the PXA168
  • Enable DMA support for the PXA168
  • Port the old unmerged PXA168 patches to a modern kernel
  • Update the WM8961 driver to work with device tree and hook it up
  • Figure out how to reference the PXA audio drivers from device tree
  • Figure out how to create an audio card (hopefully, simple-audio-card) to connect the SoC and codec together.
  • Enable ALSA tools in buildroot
  • Get the Super Mario World game over sound playing over the speakers
  • Figure out how to play sound through the headphone jack
  • Detect when headphones are plugged in

Yikes, that’s actually a lot of stuff to do. Some of it is simple, but I can see why nobody (to my knowledge) has ever bothered getting the Chumby’s audio working on modern kernels. When I’m overwhelmed with a zillion prerequisites like this, I like to break it up into baby steps and focus on one thing at a time.

Let’s start with I2C support. That should be fairly straightforward. It turns out that the mainline kernel’s device tree include file for the PXA16x already has the TWSI devices enabled! TWSI is the name used by Marvell for what is essentially their I2C controller IP. This was pretty easy to turn on in my kernel config. I just needed to enable CONFIG_I2C_PXA and turn on twsi1 and twsi2 in the device tree. As part of this I also hooked up the onboard AT24C08 “personality EEPROM” to twsi1. This would provide an easy way to verify that I2C was working. It turns out twsi2 wasn’t enabled automatically at startup, so I hacked in a fix to U-Boot to ensure they were both enabled. I think the Linux clocking should have been able to handle that, but I was more focused on just getting it working.

This all went really well. After doing that, I saw kernel messages at startup about the newly-enabled controllers and the EEPROM:

i2c i2c-0:  PXA I2C adapter
i2c i2c-1: PXA I2C adapter
at24 0-0050: supply vcc not found, using dummy regulator
at24 0-0050: 1024 byte 24c08 EEPROM, writable, 1 bytes/write

I was able to dump the EEPROM with a simple command:

cat /sys/class/i2c-dev/i2c-0/device/0-0050/eeprom > /tmp/eeprom-backup.bin

This dump file contained strings like “sexi”, “chum”, “vers”, etc. so I was pretty confident that I had succeeded. I2C was officially working! I love it when everything in the kernel just works the way it’s supposed to.

Let’s move onto DMA. DMA is important because if you’re sending a bunch of audio data over I2S, you don’t want the CPU to be in charge of manually sending all of that audio data. That would be crazy. You want to allow the DMA controller to do it in the background in order to free up the CPU to do other more important stuff. Also, it was pretty clear from looking at the existing audio code that DMA would be a dependency of the audio support.

DMA is another thing that already had a driver in the kernel. I just had to find it, and then hook it up in the device tree, which also entailed specifying the DMA channels for the various peripherals. I filled them out for the SSP devices. I also had to enable the driver in the kernel config.

Amazingly enough, this all just seemed to magically work:

pxa-dma d4000000.dma-controller: initialized 32 channels on 100 requestors

The kernel was spitting out a bunch of errors at startup though:

pxa-dma d4000000.dma-controller: error -ENXIO: IRQ index 1 not found
pxa-dma d4000000.dma-controller: error -ENXIO: IRQ index 2 not found
pxa-dma d4000000.dma-controller: error -ENXIO: IRQ index 3 not found
pxa-dma d4000000.dma-controller: error -ENXIO: IRQ index 31 not found

I later ended up discovering that these errors were actually harmless and I submitted a patch to the upstream kernel to fix it. The extra IRQs are optional — only IRQ 0 is used on the PXA168. Thus, the driver needed to be using platform_get_irq_optional() instead of platform_get_irq(). This fix landed in kernel 6.1 and was backported to a bunch of older kernels.

By the way, if you look closely at my submitted patch for the DMA bug, I screwed up the “Fixes” tag and forgot to add a closing “)” to the end of the line. I think the existing parentheses near the end of the line confused my brain. Luckily it was caught before it made its way to Linus for Linux 6.1-rc5.

I thought I would need to actually make some progress on the audio in order to test the DMA, but I figured out that DMA was already working at this point. That’s because I hooked up the correct DMA channels to the SSP controllers, and as you may recall from the last post, the touchscreen uses SPI. It turns out that using DMA with the touchscreen actually bogged it way down. The touchscreen still worked, but it had become much less responsive after I turned on DMA.

This puzzled me for a brief moment, and then a light bulb went off in my head. My touchscreen driver does very, very small SSP transactions (16 bits) one at a time in a thread. For small individual transactions like this, going through the whole process of setting up DMA ended up being much slower than just letting the CPU do it. I worked around this by increasing the minimum DMA burst size in the SSP driver. It had been set up to use DMA even on 1-byte transactions. I pulled a number out of thin air and decided to tell it to only use DMA on transactions that are at least 8 bytes. That completely fixed the touchscreen. I was still glad this happened because it helped me verify that DMA was working.

Next up, I knew that the WM8961 audio codec would be needed in order to get this setup working. All I needed to do was add support for device tree to the driver. I decided to send this change straight to the mainline kernel, just in case someone might try to remove the driver due to it not being used anywhere. The actual code was pretty straightforward — the hardest part was actually getting the device tree documentation correct. It took me two tries to get it merged because I made a few mistakes in my first attempt (V1, V2). Sometimes the code review process can be a bit intimidating, but at the end of the day it’s all about ensuring good quality of code and shouldn’t be taken personally. I’m glad this wasn’t my first submitted patch or I might have been scared away!

That took care of most of the low-hanging fruit. With DMA and I2C working, and the codec available to add to the device tree, I had most of the prerequisites out of the way so that I could start tinkering with actual audio stuff. This is where it started to get more difficult to split the work up because there were interdependencies between the different components, so things got a little hectic. I’m still in a little bit of disbelief that I figured this all out, so it might be difficult to logically explain it all. I’ll try to step through it as best as I can.

I did a bunch of research and noticed that there had been some prior patches to add support for the PXA168 to ASoC. In particular, this one was pretty interesting because it had a table with a bunch of hardcoded register setups for various bit rates. I was pretty sure I’d need something similar in order to configure the PXA168 to have correct MCLK and bit clock frequencies.

I basically started tossing things together to try to get it working. I ensured the ASoC pxa-ssp driver was allowed to be enabled with this architecture, and then added a port of the custom PXA168 logic in a couple of commits (1, 2). Then I tried to mix it all into a simple-audio-card in the device tree. I didn’t really know what I was doing here, but I used other boards as examples to try to walk myself through it. The math being used in the MCLK/BCLK tables in the original patch supplied by the Marvell employee confused me, so I went with my own calculation based on an MCLK of 256Fs as discussed earlier. With 16 bits per sample and 2 channels, this meant I would need to divide MCLK by 256/16/2 = 8 to end up with the correct bit clock.

I knew I would need to configure SSP1 to use this new configurable clock as opposed to one of the hardwired constant clocks it can use, so I took care of that as well. I opted to reconfigure the clock source in U-Boot out of simplicity. I’m thinking that the common clock framework would have been a better place for all of this clocking setup. At this point I just wanted to get it working!

With everything sort of hooked up, I gave it a shot. I tried to play my sound with aplay:

aplay /smw_game_over.wav

As expected, it didn’t work right out of the gates.

wm8961 1-004a: MCLK has not been specified
wm8961 1-004a: ASoC: error at snd_soc_dai_hw_params on wm8961-hifi: -22
ssp-dai0-wm8961-hifi: ASoC: __soc_pcm_hw_params() failed (-22)

I believe I had the clocking hooked up wrong, and just hacked something together by adding:

system-clock-frequency = <11289600>;

…to the codec section of the simple-audio-card, but that was just a temporary stopgap solution. That number is exactly 256 times 44,100 (44.1 kHz). I didn’t take the best notes about what I did here because I was trying all kinds of things, but I eventually didn’t need that line anymore. I believe this ended up being the better solution so that it worked properly with different bit rates:

mclk-fs = <256>;

This got me a bit further. Next I was getting a null pointer dereference:

Unable to handle kernel NULL pointer dereference at virtual address 00000000
[00000000] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT ARM
Modules linked in: libertas_sdio libertas sha256_generic libsha256 cfg80211 snd_soc_pxa_ssp snd_soc_simple_card rfkill snd_pxa2xx_lib snd_soc_wm8961 snd_soc_simple_card_utils snd_pcm_dmaengine snd_soc_core snd_pcm chumby8_ts snd_timer snd soundcore spi_pxa2xx_platform ssp
CPU: 0 PID: 193 Comm: aplay Not tainted 5.18.9 #3
Hardware name: Marvell PXA168 (Device Tree Support)
PC is at snd_dmaengine_pcm_get_chan+0x14/0x1c [snd_pcm_dmaengine]
LR is at pxa2xx_pcm_hw_params+0x2c/0xec [snd_pxa2xx_lib]
snd_dmaengine_pcm_get_chan [snd_pcm_dmaengine] from pxa2xx_pcm_hw_params+0x2c/0xec [snd_pxa2xx_lib]
pxa2xx_pcm_hw_params [snd_pxa2xx_lib] from pxa2xx_soc_pcm_hw_params+0x18/0x1c [snd_pxa2xx_lib]
pxa2xx_soc_pcm_hw_params [snd_pxa2xx_lib] from snd_soc_pcm_component_hw_params+0x48/0xb8 [snd_soc_core]
snd_soc_pcm_component_hw_params [snd_soc_core] from __soc_pcm_hw_params+0x504/0x6a8 [snd_soc_core]
__soc_pcm_hw_params [snd_soc_core] from soc_pcm_hw_params+0x38/0x50 [snd_soc_core]
soc_pcm_hw_params [snd_soc_core] from snd_pcm_hw_params+0x158/0x420 [snd_pcm]
snd_pcm_hw_params [snd_pcm] from snd_pcm_ioctl+0x280/0x19ac [snd_pcm]
snd_pcm_ioctl [snd_pcm] from sys_ioctl+0x538/0xcd4
sys_ioctl from ret_fast_syscall+0x0/0x1c

Inspecting the code further, I tracked the problem down to being caused by pxa2xx_pcm_open() returning early because snd_soc_dai_get_dma_data() was returning null. Comparing the pxa-ssp driver to other similar drivers, it seemed different. A lot of other drivers set up the DMA in their probe functions, but the pxa-ssp driver was doing it in the startup/shutdown functions. I simply changed the pxa-ssp driver to be more like the others and set up the DMA in the probe function. Was this the correct solution? I don’t know — but the fact that other drivers did it this way made me feel like I was going in the right direction. It definitely fixed the issue.

With this in place, I actually got something to play some sound — sort of. It never worked the very first time I would try to play a sound. I had to remove and reinsert the kernel module in order to get it to work. Also, the pitch was wrong, and it sounded kind of scratchy, but hey, it was playing a sound!

I was totally stoked! I got sound working! It wasn’t perfect, but at this point I felt confident I would be able to figure it out. I tackled the various problems one by one. It turns out that I had to add special code in the PXA168 SSP setup to manually turn on MCLK immediately, because by default the WM8961 can’t be configured unless it’s actively receiving MCLK. This solved the issue of sound only working after the second time the kernel module was loaded.

As for the incorrect pitch, the SSP controller was being set up with an initial “dummy” bit, so it was sending out 17 bits per sample instead of 16. This caused the sound to play slower. There are different data formats supported by the WM8961 including I2S mode, left-justified, and right-justified. I2S mode is weird in that it expects LRCLK to change 1 cycle before the actual data for the new channel should be transmitted. I believe this was the reason for the dummy bit. However, adding it caused a total of 17 bits to be sent per sample instead of 16. I’m still not sure if this is a mistake in the Linux driver for all PXAxxx chips, or if the PXA168’s controller behaves slightly differently from the others. It’s hard to know for sure without other PXA2xx/3xx hardware to test with.

Either way, the solution was easy: switch to left-justified mode instead. Left-justified mode is very similar to I2S mode, except the new data starts at the exact same time as the LRCLK transition, and the LRCLK polarity is inverted. What I mean by inverted is that in left-justified mode, a high LRCLK means it’s left channel data, whereas in I2S mode, a high LRCLK means it’s right channel data.

Since the modes are very similar, it was easy enough to simply add left-justified support to the PXA audio driver. I do feel like my change was a little messy for this though.

With left-justified mode implemented, I actually got perfect audio playback! The scratchiness and incorrect pitch were gone.

As you can see, sometime during this process I also changed to the actual Chumby logo. The colorful screen in the first video was useful for making sure I had the colors mapped correctly in the LCD controller.

I probably could have called it good enough at this point, but it was around now that I remembered the Chumby 8 also has a microphone. I couldn’t resist trying to get the microphone working. At face value it didn’t seem too scary, especially since a Chumby developer had provided some sample commands used for their factory microphone testing. Of course, this led me down another crazy rabbit hole.

It started out simple. I hooked up the microphone and MICBIAS in device tree, which did take a bit of research and looking at other example boards, but it wasn’t too bad. Then I tried recording but it didn’t work at all. I eventually discovered that the mic did work, but I had to record in stereo, and the microphone data was showing up in the right channel instead of the left channel. This led me to realize that I actually had LRCLK inverted from what it was supposed to be. That was important because the mic is mono and only hooked to the left channel in the Chumby. I hadn’t noticed when I switched to left-justified mode that LRCLK needed to be backwards from how it was set up in I2S mode. That was pretty simple to fix.

That was only the beginning of this saga though. After I did that, I noticed that when I was playing audio, the data for the two audio channels was being sent out of order. I was sending left sounds to the right speaker and vice-versa. So my backwards LRCLK was just happening to work previously because I was providing the data in the wrong order! I couldn’t figure out how to swap the two 16-bit channels before sending them out over I2S. It’s possible that there’s some magic solution to tell the DMA controller to swap the 16-bit channels in each 32-bit word being transmitted. I don’t know — I couldn’t figure it out.

I ended up reading in detail about the SSP controller in the PXA168. In particular I noticed that it supports something called network mode, where you can allocate time slots dedicated for data to be sent. It turns out the Linux driver actually used to use network mode but eventually switched away from it. It was unclear to me exactly why this change was made in 2009. The PXA168 datasheet specifically says network mode is for emulating the I2S protocol. I’m sure there was a very good reason for switching away, but I found that network mode was just what I needed for ensuring the data was being sent out in the correct order. It wasn’t too difficult to switch the driver over, and it allowed me to remove an ugly hack I had needed for the old method.

With the change to network mode all implemented, my rabbit hole journey was complete and audio playback and recording worked great, right?

Wrong. Somehow I managed to discover another bug. Sometimes if I played a mono sound with aplay, the left channel sounded fine but the right channel would play back as random static noise instead of silence like it was supposed to. The weird part is it would only happen after I had tried recording! And sometimes recording would fix the issue and make the static go away.

I was intentionally disabling the right channel when playing back mono sound. There shouldn’t have been any data at all on the right channel. But something was causing it to play back random noise even though it was supposedly disabled. And why would it only happen after recording?

I still have no explanation for why this happened. It smells like a silicon bug to me, but it might just be my lack of understanding of how the SSP peripheral works. Maybe weirdness like this was why the kernel developers switched away from network mode. I really wanted to use network mode, though. It was perfect for what I needed, aside from this strange bug. What I ended up doing was removing the second channel altogether when playing back mono sound, rather than disabling it. Removing the channel and adding 16 bits of dummy data to replace it completely solved the issue. This fix was included with my final commit I linked earlier.

So yeah. If I’m being 100% honest with myself, I’m still not totally sure how I got this all working or if my solution is correct. I came up with a somewhat hacky fix that works, but seems to go against intentional decisions made by kernel developers 15 years ago. I can play audio reliably and the microphone works too. I’m writing this post over a year after I first figured it out, and I’m still afraid of even thinking about trying to upstream my audio fixes. I can’t provide great reasons for why the random issues I saw happened, and I have no idea whether my fixes are truly the best approach. I think at minimum, I would need to clean up the clocking to use the common clock framework. Also, I would need to test against other PXA2xx/3xx devices to see if they are also affected by the “17 instead of 16 bits” pitch/speed shift issue. I feel like other kernel developers would have noticed that though, so I’m tempted to assume it’s something weird about the PXA168. The bottom line is that I think for the foreseeable future, my PXA audio fixes are going to stay in my branch.

By the way, I also got headphone jack detection working. It was super easy to set up. This commit I linked earlier adds the detection. All I had to do was add this line to the correct section of my device tree:

simple-audio-card,hp-det-gpio = <&gpio 97 GPIO_ACTIVE_LOW>;

The mapping of GPIO 97 matches the schematic diagram I showed at the start of this post. That was really easy! Back in the 2.6.28 days, the Chumby kernel had Chumby-specific hacks in the WM8961 driver to add headphone plugin/removal detection. It was pretty cool seeing how much simpler things have gotten since then. A one-line addition to the device tree is a lot easier than vendor-specific kernel hacks!

Here’s a quick demo of how the headphone detection works. It creates an input device that you can look at to detect whether headphones are plugged in. evtest is a simple utility that can monitor it:

# evtest
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: Chumby Speaker Headphones
/dev/input/event1: Chumby 8 touchscreen
Select the device event number [0-1]: 0
Input driver version is 1.0.1
Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0
Input device name: "Chumby Speaker Headphones"
Supported events:
Event type 0 (EV_SYN)
Event type 5 (EV_SW)
Event code 2 (SW_HEADPHONE_INSERT) state 0
Testing ... (interrupt to exit)
Event: time 1713155219.026612, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 1
Event: time 1713155219.026612, -------------- SYN_REPORT ------------
Event: time 1713155224.496627, type 5 (EV_SW), code 2 (SW_HEADPHONE_INSERT), value 0
Event: time 1713155224.496627, -------------- SYN_REPORT ------------

I started out with no headphones inserted, then plugged them in, and finally removed them 5 seconds later. I believe there is userspace software that will recognize this particular switch event and knows to enable/disable the headphone and speaker as necessary. I can also manually control headphone and speaker volume independently with amixer:

amixer set Headphone 25%
amixer set Speaker 70%

I should also add that I was concerned about any possible differences between the old kernel and the new kernel in terms of how the WM8961 was set up. The WM8961 setup is basically just a bunch of register writes over I2C. I painstakingly compared what the old and new kernels were doing. It seemed like Chumby’s kernel defaulted to a higher speaker AC gain (7 instead of 3), and also did some tweaks to the volume and mic bias controls to bring them out to the user separately and limit the volumes. Nothing seemed too crazy. I was mostly concerned about trying to drive the speakers louder than they should be, so for now I’ll just make sure to not drive the volume too high.

Interestingly, it appears the Infocast and Chumby 8 had slightly different limits. The Infocast was limited to a maximum of 121 for the headphone volume and 119 for the speakers, whereas the Chumby 8 had the full limit of 127 on both of them. I don’t know if that was a requirement from Insignia, or if the hardware changed a bit between revisions. Either way, it seems like with my default gain of 3, it sounds fine even when I go up to 127 on the Infocast.

Whew! That was a lot to figure out. What it comes down to is I struggled a lot with implementing audio support on the Chumby 8, but I got it working well enough. This was probably the most difficult part of my entire Chumby kernel modernization. I think the main reason for that is my lack of experience with ALSA. It was still pretty impressive that the vast majority of the infrastructure for audio support on the PXA168 was already there in the mainline kernel. I mainly just had to add a little bit of glue code for the PXA168 and fix some random issues. I’d still love to know whether other PXA devices using the same driver play their sounds slightly too slowly.

At this point with the Chumby 8’s display, touchscreen, Wi-Fi, and audio working, there wasn’t much left to do, but I’m not done writing about this project yet. I’ll give you a little teaser for the next post. I’d like to talk about a strange issue I noticed. The CPU was always showing up as being 100% loaded, even with nothing running. The original Chumby 2.6.28 kernel definitely didn’t do this. What was happening to cause the CPU to be so busy? Stay tuned for the next post to find out all about it.

Click here for part 9.



  1. Thanks for continuing this series- really impressive that you got the audio working, including the microphone.

  2. Thank you Steve! I apologize for taking so long to continue the series. I was dreading the process of writing this one. It was super complicated and it happened over a year ago, so it was hard to get everything written down. It shouldn’t take quite so long to get around to writing the next one!

  3. Hey, thank you for all those blog posts, they are really well written and interesting to read!

  4. Thanks Oliv! I’m glad that you enjoy them!

  5. Wayne Seltzer @ 2024-05-01 11:56

    This is great! Thanks for sharing. Hoping I can leverage your work to turn my Chumby
    into something useful.

  6. I hope it ends up being useful for you, Wayne!

Add your comment now