Fundamentals 10 min read

How Linux Detects Headphone Plug/Unplug Events via the ASoC Jack Driver

This article explains the Linux kernel's built‑in headphone jack detection mechanism, detailing the asoc_simple_init_jack function, device‑tree GPIO lookup, jack registration, event reporting flow, and highlights platform‑specific alternatives for Rockchip and MediaTek hardware.

Liangxu Linux
Liangxu Linux
Liangxu Linux
How Linux Detects Headphone Plug/Unplug Events via the ASoC Jack Driver

Linux built‑in headphone jack detection

The Linux kernel mixes headphone plug/unplug detection into the sound card driver, reporting the state through the input subsystem. The core implementation resides in kernel-5.15/sound/soc/generic/simple-card-utils.c, particularly the asoc_simple_init_jack function.

int asoc_simple_init_jack(struct snd_soc_card *card,
    struct asoc_simple_jack *sjack,
    int is_hp, char *prefix, char *pin)
{
    struct device *dev = card->dev;
    enum of_gpio_flags flags;
    char prop[128];
    char *pin_name;
    char *gpio_name;
    int mask;
    int det;
    if (!prefix)
        prefix = "";
    sjack->gpio.gpio = -ENOENT;
    if (is_hp) {
        snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
        pin_name = pin ? pin : "Headphones";
        gpio_name = "Headphone detection";
        mask  = SND_JACK_HEADPHONE;
    } else {
        snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
        pin_name = pin ? pin : "Mic Jack";
        gpio_name = "Mic detection";
        mask  = SND_JACK_MICROPHONE;
    }
    det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
    if (det == -EPROBE_DEFER)
        return -EPROBE_DEFER;
    if (gpio_is_valid(det)) {
        sjack->pin.pin  = pin_name;
        sjack->pin.mask = mask;
        sjack->gpio.name = gpio_name;
        sjack->gpio.report = mask;
        sjack->gpio.gpio = det;
        sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
        sjack->gpio.debounce_time = 150;
        snd_soc_card_jack_new(card, pin_name, mask,
            &sjack->jack, &sjack->pin, 1);
        snd_soc_jack_add_gpios(&sjack->jack, 1, &sjack->gpio);
    }
    return 0;
}
EXPORT_SYMBOL_GPL(asoc_simple_init_jack);

Lines 589‑593 construct the device‑tree property name prop used to locate the GPIO, set pin_name (e.g., "Headphones"), gpio_name (e.g., "Headphone detection"), and the jack mask ( SND_JACK_HEADPHONE). Line 601 retrieves the GPIO descriptor with of_get_named_gpio_flags. If the GPIO is valid, the function fills the sjack structure, registers the jack with snd_soc_card_jack_new, and binds the GPIO via snd_soc_jack_add_gpios.

Convenient macros in include/sound/simple_card_utils.h simplify calls:

#define asoc_simple_init_hp(card, sjack, prefix) \
    asoc_simple_init_jack(card, sjack, 1, prefix, NULL)
#define asoc_simple_init_mic(card, sjack, prefix) \
    asoc_simple_init_jack(card, sjack, 0, prefix, NULL)

During the sound card probe, the driver matches compatible = "simple-audio-card" in the device tree and invokes these macros to initialise headphone and microphone jacks.

Jack registration and event flow

The function snd_soc_card_jack_new creates a snd_soc_jack object, initialises its mutex, and registers the jack with the core ALSA jack subsystem. If pins are supplied, it forwards them to snd_soc_jack_add_pins:

int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
    struct snd_soc_jack_pin *pins)
{
    int i;
    for (i = 0; i < count; i++) {
        if (!pins[i].pin) {
            dev_err(jack->card->dev, "ASoC: No name for pin %d
", i);
            return -EINVAL;
        }
        if (!pins[i].mask) {
            dev_err(jack->card->dev, "ASoC: No mask for pin %d (%s)
", i, pins[i].pin);
            return -EINVAL;
        }
        INIT_LIST_HEAD(&pins[i].list);
        list_add(&(pins[i].list), &jack->pins);
        snd_jack_add_new_kctl(jack->jack, pins[i].pin, pins[i].mask);
    }
    snd_soc_jack_report(jack, 0, 0);
    return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);

When the GPIO changes, the kernel calls snd_soc_jack_report, which updates the jack status, enables or disables the corresponding DAPM pins, notifies listeners, and finally forwards the status to the generic ALSA jack via snd_jack_report:

void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
{
    struct snd_soc_dapm_context *dapm;
    struct snd_soc_jack_pin *pin;
    unsigned int sync = 0;
    if (!jack)
        return;
    dapm = &jack->card->dapm;
    mutex_lock(&jack->mutex);
    jack->status &= ~mask;
    jack->status |= status & mask;
    list_for_each_entry(pin, &jack->pins, list) {
        int enable = pin->mask & jack->status;
        if (pin->invert)
            enable = !enable;
        if (enable)
            snd_soc_dapm_enable_pin(dapm, pin->pin);
        else
            snd_soc_dapm_disable_pin(dapm, pin->pin);
        sync = 1;
    }
    blocking_notifier_call_chain(&jack->notifier, jack->status, jack);
    if (sync)
        snd_soc_dapm_sync(dapm);
    snd_jack_report(jack->jack, jack->status);
    mutex_unlock(&jack->mutex);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_report);

The low‑level snd_jack_report updates the ALSA control interface and, if configured, reports input events (e.g., key presses) to the Linux input subsystem.

Platform‑specific considerations

The generic driver works on many boards but has limited features. Vendors often provide their own detection logic, such as Rockchip’s implementation in kernel/drivers/headset_observe/rockchip_headset_core.c and MediaTek’s in kernel/drivers/misc/mediatek/accdet/ (e.g., kernel/sound/soc/codecs/mt6xxx-accdet.c).

To use the built‑in driver, declare the appropriate GPIO in the sound card’s device‑tree node; the kernel will then automatically configure the detection logic during driver load.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

LinuxaudioGPIOdriverASoCJack Detection
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.