June 29, 2013

Android Jellybean on i.MX6 with no disclaimers

Jelly beansIn our last post about Jellybean on our i.MX6 boards, we had a big, bold disclaimer that this was alpha code.

We’ve now spent the time to walk through many of the details and are happy to release updates and images that bring the full Android Jellybean experience without the big fat warnings.

This release is now every bit as functional as our Ice cream sandwich releases and ready for serious field testing.


For the impatient

To enable quick testing without going through the build process, we’ve uploaded two SD card images to our Cloud storage site:

Note that even though it’s slower, there are a handful of benefits to using the .img file:
  • We’ve already run dexopt on the image. During the first boot of Android, the system will go through all of the installed packages, and this slows up the first boot dramatically.
  • We added some movie trailers, so the Gallery app comes up right away.
  • We disabled the screen blanking.
  • We added some desktop shortcuts.

Highlights

  • Browser performance has been fixed.
  • SATA is supported
  • Fully boots to either SD card
  • Both parallel and MIPI cameras are supported
  • Wi-Fi on Nitrogen6x is supported and enhanced from ICS
  • Resistive touch screen is supported
  • Dual-screen (LCD+HDMI) is now supported for all panels

Notable omissions

In the intro, we said there were no disclaimers, but there are still some things on the To-Do List:
  • Nitrogen6-Lite is not yet supported
  • Bluetooth is not yet functional on Nitrogen6X

Installing from the tar-ball

To install from the tar-ball, you’ll need to extract the image, then run the mksdcard.sh script to partition and format either a SATA drive or an SD card.

In general, the process goes like this:
~/$ mkdir android-jb
~/$ cd android-jb
~/android-jb$ sudo tar zxvf ~/Downloads/imx6-jb-20130626.tar.gz
~/android-jb$ sudo device/boundary/mksdcard.sh /dev/sdc nitrogen6x
The example above assumes that your SD card or SATA drive is connected to /dev/sdc. It’s also common for machines with built-in SD card readers to have them show up as /dev/mmcblk0 or /dev/sdd.

If you want to run from SATA, you can use our USB image, but the SATA drive won’t show up as a removable disk and you’ll need to add a parameter of --force to the command-line:
~/android-jb$ sudo device/boundary/mksdcard.sh /dev/sdc nitrogen6x --force
You’ll also want to be extra careful to make sure you have the right disk. Because mksdcard.sh will re-write the partition table of the device you point it at, this can and will trash your hard drive if not used properly.

Don’t say we didn’t tell you.

Installing from the .img file

Under Linux using dd

This is probably the easiest way to install, but won’t use the entire SD card or SATA drive because SD cards vary in size and we’ve configured the image at 3.8 GiB.

Using a 4+GB SD card that appears as /dev/sdc, you can install the image like this:
~/$ gunzip ~/Downloads/imx6-sdcard-20130626.img.gz
~/$ sudo umount /dev/sdc?
~/$ sudo dd if=~/Downloads/imx6-jbsdcard-20130628.img of=/dev/sdc
~/$ sync

Under Windows

To install the image under Windows, we reccommend using Alex Page’s nice USB Imaging Tool.

The following shows the process from the command-line if your SD card appears as drive H:.
C:>usbitcmd l
USB Image Tool 1.58
COPYRIGHT 2006-2011 Alexander Beug
http://www.alexpage.de
  Device   | Friendly Name             | Volume Name | Volume Path | Size
 ------------------------------------------------------------------------------
      2500 | Generic- SD/MMC USB Devic | CE7         | H: |   3837 MB
C:>
C:>usbitcmd r 2500 imx6-jb-sdcard-20130628.img.gz /g /d
USB Image Tool 1.58
COPYRIGHT 2006-2011 Alexander Beug
http://www.alexpage.de
Restoring backup to "Generic- SD/MMC USB Device" (H:)...ok
Note that you should use Windows Explorer to eject the card when complete, and that you may need the card to be formatted to allocate a drive letter.

Also note that usbitcmd must be run as Administrator, since it requires access to the entire drive.

Getting access to the sources

As discussed in this post, you’ll need to register and forward an SSH public key in order to get access to some of the closed-source bits of the Android build tree.

Once that’s done, retrieving the sources follows the standard Android process, using branch jb-1.1.0:
~/$ mkdir myandroid
~/$ cd myandroid
~/myandroid$ repo init 
    -u git://github.com/boundarydevices/imx-android-r13.4-ga.git 
    -b jb-1.1.0
~/myandroid$ repo sync
~/myandroid$ . build/envsetup.sh
~/myandroid$ lunch
         ---select Nitrogen6x eng or user---
~/myandroid$ m 2>&1 | tee build.out
If you already have a repo tree from us, you can simply sync and switch branches like so:
~/myandroid$ repo sync
~/myandroid$ repo init -b jb-1.1.0

Usage notes

Here are some things you should be aware of when using this image:
  • The /sdcard mount point doesn’t seem to mount on the first boot when installing from the tar-ball. There seems to be some amount of setup needed during that first boot cycle, so you’ll see a message about “No storage available” if you run the Gallery application during that boot cycle.
  • Similarly, the Gallery application seems to want a boot cycle after loading up videos through adb. In other Android releases, we’ve used third party tools to force a media scan at other times, but haven’t loaded that onto this release.
  • We’ve seen some intermittent issues with audio output that need closer scrutiny. In particular, it appears that a cold boot is sometimes needed to restore the proper function of the SGTL5000.

The details

For those interested in the details of what’s been changed from the Freescale release, and how we solved some of the issues encountered, the sections below will list each of the major hurdles and the details of the solution.

  • Wi-Fi state machine changed again
  • Browser performance sucked
  • SATA support wasn’t present
  • MIPI
  • init crashes during startup
  • Touch calibration wasn’t functional

Wi-Fi state machine changed

To describe this well, we need to start with a little background about the TiWi-BLE SDIO WiFi/BT modules we use on the Nitrogen6x. This highly integrated module is easy to integrate from a hardware standpoint, but we’ve struggled with one of the pins associated with WiFi.

The Wireless LAN Enable, or WL_EN pin is used to enable the internal Wi-Fi portion of the module, and in many O/S releases, we’ve simply asserted the signal at startup and left it alone.

The trouble is that the wl12xx device driver expects this to be available and to de-asserted when the network adapter is brought down and re-asserted when the device is brought up. In addition, the driver needs the signal to be asserted when the kernel module is loaded.

There’s code in the driver to allow a platform (a board) to over-ride this, but it doesn’t function properly.

The stock Android WiFi manager not only brings the adapter up and down, it also loads and unloads the kernel module, which causes a segmentation fault or kernel panic if the platform override is used.

In the ICS release, we thought we’d dealt with this once and for all by hacking up hardware/libhardware_legacy/wifi/wifi.c to assert and de-assert the WL_EN pin at the appropriate times.

This didn’t work with Jellybean.

In our first port, the WiFi device got stuck in scenarios where the WL_EN pin was de-asserted but the driver was still trying to communicate with the chip.

While digging through the details of the various call chains, we stumbled on the origin of the problem. The SDHCI spec seems to define a power control mechanism that’s called at precisely the right points. Unfortunately, the i.MX6 Secure Digital Host Controller doesn’t support it. Instead, we need to toggle a GPIO pin at those points.

So that’s what we did in this release. We implemented a platform-specific callback at the SD-card layer, and defined one for the Nitrogen6X.

Once that was done, things just worked, and we also found that the calibrator utility worked (an issue that’s been around a long time).


Browser performance sucked

The web browser performance of both ICS and JB on our boards has been pitiful until this release.

App not responding or ANR messages could be expected, even when loading pages without a lot of dynamic content and the reason(s) weren’t clear.

Once we dug into it though, a partial answer became immediately apparent: the problem stemmed from I/O. Specifically, SD card I/O.

We had several customers report that using a Class 10 SD card improved the situation, but our testing showed only moderate improvements.

Tracing the I/O, we found that the majority of activity was in /data/data/com.android.browser/cache and we saw dramatic improvements if we mounted this directory as a RAM disk, but we still found some slowness due to I/O in /data/data/com.android.browser/databases.

In this release, we’re mounting all of /data/data/com.android.browser/ as a 32MiB RAM disk, and things are very fast.

Note that this does have a down-side. Re-visiting a page after a system restart will be slower. The system will also prompt you about sharing your location and such-like on every boot. For our immediate purposes, this is a reasonable trade-off.


SATA support

The Linux kernel for i.MX6 has had functional SATA since the beginning of i.MX6-time, but we hadn’t booted an Android image over SATA until this release.

Under ICS, there were a bunch of hard-coded mount statements in init.rc, and under JB, the file fstab.boundary contained hard-coded references to the boot device.

On both platforms, the file /system/etc/vold.fstab contained references to which device should be used for storage of media content.

In this release of Jelly Bean, we’ve changed the fstab.boundary file to have symbolic boot device ($BD) references and added support for this to the mount_all command.

diff --git a/nitrogen6x/fstab.boundary b/nitrogen6x/fstab.boundary
index 977fc45..4f9e60a 100644
--- a/nitrogen6x/fstab.boundary
+++ b/nitrogen6x/fstab.boundary

-/dev/block/mmcblk0p1    ...
-/dev/block/mmcblk0p5    ...
-/dev/block/mmcblk0p4    ...
-/dev/block/mmcblk0p6    ...
-/dev/block/mmcblk0p7    ...
+$BD1    /boot    vfat   ...
+$BD5    /system  ext4   ...
+$BD4    /data    ext4   ...
+$BD6    /cache   ext4   ...
+$BD7    /device  ext4   ...
Note that this doesn’t complete the job of adding SATA support. If you’re booting SATA, you’ll also need to edit /system/etc/vold.fstab. More specifically, you’ll need to comment out this line:
dev_mount sdcard /mnt/sdcard 4 /devices/platform/sdhci-esdhc-imx.2/mmc_host/mmc1
and un-comment (remove the #-sign from) this line:
# dev_mount sdcard /mnt/sdcard 4 /devices/platform/ahci.0/host0/target0...
Refer to the comments in the vold.fstab file on Github to get a better understanding.

MIPI camera support

As briefly described our quick MIPI post, there are some resource conflicts that prevent us from supporting both our parallel OV5642 camera and OV5640 MIPI camera modules at the same time (with the same kernel). Until we have a more elegant solution, we’ve added support for the MIPI camera module through a kernel defconfig.

In this release of Android, we’ve extended this support to the build process through a compile command-line variable DEFCONF. By assigning this variable at build time (when invoking make or m, you can build your kernel with the MIPI configuration. In other words, if you do this:
~/myandroid$ m DEFCONF=nitrogen6x_mipi_defconfig 2>&1 | tee build.out
instead of this:
~/myandroid$ m 2>&1 | tee build.out
Your kernel will be built with MIPI support instead of the parallel camera.

The rest of Android is built to allow both cameras simultaneously, but we won’t be able to do that without a hardware spin and/or some additional kernel work.

init was crashing at startup

This was a weird one, and seems to have been around a while.

We were ready to ship a pre-release to a customer and found that an SD card that worked on one board didn’t work when prepping for shipment.

Was it the monitor? Was it the board?

After a bunch of swapping around parts, we found that it was actually a mouse used in our lab.

root@android:/sys/class/input # cat event1/device/name
Logitech USB-PS/2 Optical Mouse
Digging through the details of the crash, we found a buffer overrun in the file hardware/imx/libsensors/SensorBase.cpp that was crashing while looking for an accelerometer by screen-scraping /proc/bus/input/devices.

We fixed that bug, and also realized that we didn’t need that component anyway.

Belt and suspenders.


Touch calibration wasn’t functional

In our first release of Jelly Bean, we completely skipped a set of changes we made to the touch screen calibrator program.

In the ICS releases and in Froyo and Gingerbread before then, we added support for passing a touch screen device name to the Android startup program and executed it during boot.

We didn’t realize that this involved a change by Freescale that added support for the exec command.

A simple cherry-pick of these patches didn’t do the trick, so we bailed out a little bit.

Since our 800×480 panels can operate quite well with a single calibration value, and the tsc2004 touch controller used with that panel supports a kernel command-line parameter with the calibration, we added a default calibration parameter into the Jelly Bean boot script. Note that you can still over-ride this with the calibration environment variable.

We also added the ts_calibrator program into our build, along with all of our ts_calibrator patches, so you can still run calibration.

It just won’t happen automagically.

To run the calibration program by hand, you can do this from an Android shell:
root@android: / # stop
root@android: / # echo 0,0 > /sys/class/graphics/fb0/pan
root@android: / # ts_calibrator
root@android: / # cat /data/system/calibration | busybox xargs echo
-67247,-764,272499173,324,69283,-8653010,65536
The output string is the set of calibration constants.

You can have this take effect immediately for testing by handing it to the driver like this:
root@android: / # echo -67247,-764,272499173,324,69283,-8653010,65536 
                > /sys/module/tsc2004/parameters/calibration
The resulting string can be saved in U-Boot like this:
U-Boot > setenv calibration -67247,-764,272499173,324,69283,-8653010,65536
U-Boot > saveenv
While not as convenient as the automatic startup, this process works pretty well.