[Galaxy S I9000 General] miki4242:
Getting stereo FM radio working: updates

UPDATED: Summary and documentation.

UPDATED: Replaced WM8990 datasheet with one for the WM8991.

Summary

Lots of people reported that listening to the Galaxy S I9000's FM radio through earphones appears to produce so-called 2-channel mono sound, i.e. sound comes from both Left and Right channels, but it is exactly the same, whether the broadcast actually is stereo or not. However, tests by lpy show that the FM radio is perfectly capable of receiving stereo broadcasts, but somehow only the Left channel is routed to both Left and Right earphones. This isn't even a proper mono mixdown, because that should mix both channels together before output.

Analysis

Investigation of the firmware and the kernel source code supplied by Samsung indicate that the I9000 uses a Silicon Labs Si4709 FM radio chip, which according to its datasheet can output stereo audio on a pair of analog outputs.

The I9000 uses a Wolfson Microelectronics WM8994 audio hub chip for sound recording, mixing and playback. At least one of the Si4709's outputs is connected to an analog input on the WM8994, which communicates with the S5PC110 main CPU/GPU SoC using I2S to receive commands and send back data.

During driver development, Samsung appears to have had a problem with unintended mixing of audio from the FM radio and a mic input, for which Samsung inserted a quickfix in the driver. See the comments marked 'Temporary inserted for blocking MIC and FM radio mixing - DW Shim 2010.03.04' in sound/soc/codecs/wm8994_aries.c .

This appears to be related to receiving a call or using speech recognition while listening to the radio.

The unfortunate result of this fix appears to be that only the Left channel from the FM radio is sent to the mixer, which in turn sends that same audio out to both Left and Right earphone outputs.

edit: robert_schl has suggested that even the jack plug / device detection driver could be interfering with the issue. All this doesn't make the analysis any easier...

Resources

Silicon Labs

Webpage: link

Si4709

Full name: Smallest FM receiver with RDS

Data Short: link

Datasheet: link (Unofficial!)

Wolfson Microelectronics

Open Source website: link

ASoC - audio for embedded Linux systems: link

ASoC Git repository browser: link

ALSA Scenario Manager: link

WM8994

Full name: Multi-Channel Audio Hub CODEC for Smartphones

Datasheet: NOT available online, available on request from Wolfson Microelectronics.

Product Brief: link

Note: A datasheet for the WM8991, a similar chip, can be downloaded here: link (Thanks go to the Odroid project.)

Initial Linux kernel driver commit info: link

Drivers

WM8994 driver by Wolfson Microelectronics

Key Wolfson Development driver files

wm8994.c: link

wm8994.h: link

wm_hubs.c: link

wm_hubs.h: link

Notes

wm_hubs.c contains mixer control element definitions, which often match descriptive names to key pin assignments, chip register names and other #defines, so is very useful as documentation!

WM8994 driver by Samsung

Relevant kernel config variables, sources and modules that would be built if configured as module:

CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
CONFIG_SND_DRIVERS=y
CONFIG_SND_ARM=y
CONFIG_SND_SPI=y
CONFIG_SND_SOC=y # soc-core.c soc-dapm.c, snd-soc-core.ko
CONFIG_SND_S5P_SOC=y
CONFIG_SND_S3C_I2S_V50=y # s3c/s3c-pcm-lp.c s3c/s3c-i2s.c, s3c/s3c-pcm-lp.ko s3c/s3c-i2s.ko
CONFIG_SND_S3C_PCMDEV=y # s3c/s3c-pcm.c s3c/s3c-pcmdev.c, s3c/s3c-pcm.ko s3c/s3c-pcmdev.ko
CONFIG_SND_C110_PCM=y # s3c/universal_pcm_wm8994.c, s3c/snd-soc-universal-pcm-wm8994.ko
CONFIG_SND_UNIVERSAL_WM8994=y # s3c/universal_wm8994.c, s3c/snd-soc-universal-wm8994.ko
CONFIG_SND_UNIVERSAL_WM8994_MASTER=y
CONFIG_SND_SOC_I2C_AND_SPI=y
CONFIG_SND_SOC_WM8994=y # codecs/wm8994.c codecs/wm8994_aries.c, codecs/snd-soc-wm8994.ko

WM8994 GPIO assignments

Possibly used for Mic input source selection or for configuration of digital mic, but UNDOCUMENTED at this time.

(DANGEROUS to experiment with, it's very easy to fry electronics if you don't know exactly what's connected to them!)

Pin connections, GPIO assignments, etc.

Device WM8994 Pin GPIO Config
FM radio: IN2L(N)(/IN2R(N)?)
Headset: HPOUT1L/HPOUT1R
Headset Mic: IN1R(P&N)
Back speaker: N/A
Earpiece: N/A
Mouthpiece: N/A

Audio device mapping

There appear to be 2 audio devices, each on a different sound 'card', and a single mixer device.

According to /etc/asound.conf , Card#0 is used for all 'normal' audio input and output, while Card#1 appears to be used only for video calls. Why?

Audio routing

Initiated by opening (dummy) audio pcm's via ALSA

Defined by pcm 'hooks' of type 'ctl_elems' in /etc/asound.conf

Signal routers (exposed as enum mixer control elements)

# Audio output paths:
'FM Radio Path': {'FMR_OFF', 'FMR_SPK', 'FMR_HP', 'FMR_SPK_MIX', 'FMR_HP_MIX', 'FMR_DUAL_MIX'}
'Playback Path': {'OFF', 'RCV', 'SPK', 'HP', 'BT', 'DUAL', 'RING_SPK', 'RING_HP', 'RING_DUAL', 'EXTRA_DOCK_SPEAKER', 'TV_OUT'}
'Voice Call Path': {'OFF', 'RCV', 'SPK', 'HP', 'BT'}
# Audio input path (note: Bluetooth missing, see /etc/asound.conf):
'MIC Path': {'Main Mic', 'Hands Free Mic'}
# FM radio subsettings while in-call or using speech recognition:
'Codec Status': {'FMR_VOL_0', 'FMR_VOL_1', 'FMR_OFF', 'REC_OFF', 'REC_ON'}
# (Internal audio test mode: enable/disable wm8994->testmode_config_flag):
'Codec Tuning': {'OFF', 'ON'}

Notes on /etc/asound.conf

For the hook_args arguments, numeric values for the enums are used, but I wonder if textual values are also supported (which would reduce the risk of confusion, see below).

There appear to be some issues with /etc/asound.conf:

Several entries have numeric values for 'DUAL' and 'BT' reversed in their comments, but still use the correct values.

pcm.AndroidPlayback: has empty hook_args. Maybe this is necessary to "enable output from DSP" as the comment says?

pcm.AndroidPlayback_Earpiece_ringtone: wrong value: must be 1 for 'RCV'

pcm.AndroidPlayback_Headset_incall: 'MIC Path' missing? (see pcm.AndroidPlayback_Earpiece_incall)

pcm.AndroidPlayback_Speaker_Headset_dualinput: is identical to pcm.AndroidPlayback_Speaker_Headset_fmradio . Why?

pcm.AndroidRecord_Bluetooth_normal: 'MIC Path' value 2 is out of range ('Bluetooth Mic' isn't defined for this mixer control element)

pcm.AndroidRecord_Headset_ringtone: Shouldn't 'Playback Path' be 7 for 'RING_HP'?

pcm.AndroidRecord_*_videocall: have empty (commented-out) hook_args. Why?

Mixer controls

Defined in: sound/soc/codecs/wm8994.c

Type: static const struct snd_kcontrol_new wm8994_snd_controls[]

Audio routing control functions

Defined in: sound/soc/codecs/wm8994.c, sound/soc/codecs/wm8994_aries.c

Type: static int wm8994_get_*_path(struct snd_kcontrol *kcontrol,

struct snd_ctl_elem_value *ucontrol)

Type: static int wm8994_set_*_path(struct snd_kcontrol *kcontrol,

struct snd_ctl_elem_value *ucontrol)

Jack type / device type detection driver by Samsung

Files: arch/arm/mach-s5pc110/sec_jack.c

Requests

- Could anyone please supply us with the

full

datasheet for the WM8994?

- Can anyone shed more light on the WM8994's GPIO assignments? Yes, the GPIO registers are multi-function too, not even sure if some of them are used for non-GPIO functions, aargh!

Planning

1. Correct audio routing to attempt stereo FM radio playback

2. Build and test proof-of-concept kernel (may break phone call handling, speech recognition, etc.)

3. Correct audio routing for FM radio playback while in-call or using speech recognition.

4. (Hopefully) Build and test final release kernel

Volunteers to help with implementing and testing this are welcome!