ipod6g: Add inline earphone remote support
rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]>
| Newsgroups | gmane.comp.systems.archos.rockbox.cvs |
|---|---|
| Message-ID | <[email protected]> |
commit b217a550598e2fc7fbb76924cb6758b18bfeff83 Author: Hemant Kumar <[email protected]> Date: Tue Jul 14 20:35:07 2026 -0600 ipod6g: Add inline earphone remote support Decode the jack remote's play/pause and volume buttons via the "Mikey" controller (I2C 0x72) and report them as multimedia keys so they work on every screen, like the OF. Protocol reverse engineered on-device, notes in mikey-6g.c. Change-Id: If5f3d3abf043c0ce0d8ca7beb0f4b591e41c5c43 diff --git a/apps/misc.c b/apps/misc.c index ea4726d0c2..7aa931636d 100644 --- a/apps/misc.c +++ b/apps/misc.c @@ -809,6 +809,14 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame case BUTTON_MULTIMEDIA_STOP: list_stop_handler(); return event; + case BUTTON_MULTIMEDIA_VOLUME_UP: + case BUTTON_MULTIMEDIA_VOLUME_UP|BUTTON_REPEAT: + adjust_volume(1); + return event; + case BUTTON_MULTIMEDIA_VOLUME_DOWN: + case BUTTON_MULTIMEDIA_VOLUME_DOWN|BUTTON_REPEAT: + adjust_volume(-1); + return event; case BUTTON_MULTIMEDIA_REW: case BUTTON_MULTIMEDIA_FFWD: /* not supported yet, needs to be done in the WPS */ diff --git a/docs/CREDITS b/docs/CREDITS index d43382efb5..b7acbb46d6 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -764,6 +764,7 @@ Teun van Dalen Adam N. Burke Lê Khánh Bình Michael McAllister +Hemant Kumar The libmad team The wavpack team diff --git a/firmware/SOURCES b/firmware/SOURCES index 202575595f..3b3a62765d 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1675,6 +1675,7 @@ target/arm/s5l8702/ipod6g/serial-6g.c #ifndef BOOTLOADER target/arm/s5l8702/ipod6g/audio-6g.c target/arm/s5l8702/ipod6g/cscodec-6g.c +target/arm/s5l8702/ipod6g/mikey-6g.c #endif /* BOOTLOADER */ #endif /* IPOD_6G */ diff --git a/firmware/export/button.h b/firmware/export/button.h index dadbf4e5a2..479fd95679 100644 --- a/firmware/export/button.h +++ b/firmware/export/button.h @@ -106,13 +106,17 @@ int button_apply_acceleration(const unsigned int data); #define BUTTON_MULTIMEDIA_NEXT (BUTTON_MULTIMEDIA|0x08) #define BUTTON_MULTIMEDIA_REW (BUTTON_MULTIMEDIA|0x10) #define BUTTON_MULTIMEDIA_FFWD (BUTTON_MULTIMEDIA|0x20) +#define BUTTON_MULTIMEDIA_VOLUME_UP (BUTTON_MULTIMEDIA|0x40) +#define BUTTON_MULTIMEDIA_VOLUME_DOWN (BUTTON_MULTIMEDIA|0x80) #define BUTTON_MULTIMEDIA_ALL (BUTTON_MULTIMEDIA_PLAYPAUSE| \ BUTTON_MULTIMEDIA_STOP| \ BUTTON_MULTIMEDIA_PREV| \ BUTTON_MULTIMEDIA_NEXT| \ BUTTON_MULTIMEDIA_REW | \ - BUTTON_MULTIMEDIA_FFWD) + BUTTON_MULTIMEDIA_FFWD| \ + BUTTON_MULTIMEDIA_VOLUME_UP| \ + BUTTON_MULTIMEDIA_VOLUME_DOWN) #ifdef HAVE_TOUCHSCREEN long touchscreen_last_touch(void); diff --git a/firmware/export/config/ipod6g.h b/firmware/export/config/ipod6g.h index d1cb511bb5..3980eaa483 100644 --- a/firmware/export/config/ipod6g.h +++ b/firmware/export/config/ipod6g.h @@ -262,6 +262,8 @@ #ifndef LOGF_SERIAL #define IPOD_ACCESSORY_PROTOCOL #endif +/* Inline earphone remote, decoded by the jack "Mikey" controller */ +#define HAVE_MIKEY_REMOTE #endif /* Define this if you can switch on/off the accessory power supply */ diff --git a/firmware/target/arm/ipod/button-clickwheel.c b/firmware/target/arm/ipod/button-clickwheel.c index 4c54e556cd..fc10efc9a4 100644 --- a/firmware/target/arm/ipod/button-clickwheel.c +++ b/firmware/target/arm/ipod/button-clickwheel.c @@ -45,6 +45,9 @@ #include "pmu-target.h" #include "clocking-s5l8702.h" #endif +#ifdef HAVE_MIKEY_REMOTE +#include "mikey-target.h" /* inline earphone remote */ +#endif #define WHEEL_FAST_OFF_TIMEOUT 250000 /* timeout for acceleration = 250ms */ #define WHEEL_REPEAT_TIMEOUT 250000 /* timeout for button repeat = 250ms */ @@ -399,6 +402,9 @@ void button_init_device(void) #endif s5l_clickwheel_init(); semaphore_wait(&button_init_wakeup, HZ / 10); +#ifdef HAVE_MIKEY_REMOTE + mikey_init(); /* start inline earphone remote polling */ +#endif } bool button_hold(void) @@ -474,9 +480,14 @@ int button_read_device(void) } /* The int_btn variable is set in the button interrupt handler */ + int btn = int_btn; #ifdef IPOD_ACCESSORY_PROTOCOL - return int_btn | remote_control_rx(); -#else - return int_btn; + btn |= remote_control_rx(); #endif +#ifdef HAVE_MIKEY_REMOTE + /* not gated by the hold switch, matching the OF: the inline remote + * keeps working while the device is locked */ + btn |= mikey_button_read(); +#endif + return btn; } diff --git a/firmware/target/arm/s5l8702/debug-s5l8702.c b/firmware/target/arm/s5l8702/debug-s5l8702.c index 9b58c46dfa..fbf780b20e 100644 --- a/firmware/target/arm/s5l8702/debug-s5l8702.c +++ b/firmware/target/arm/s5l8702/debug-s5l8702.c @@ -35,6 +35,9 @@ #include "uart-target.h" #include "uc87xx.h" #endif +#ifdef HAVE_MIKEY_REMOTE +#include "mikey-target.h" +#endif #include "clocking-s5l8702.h" #define DEBUG_CANCEL BUTTON_MENU @@ -133,6 +136,14 @@ bool dbg_hw_info(void) #ifdef IPOD_ACCESSORY_PROTOCOL _DEBUG_PRINTF("accessory present: %s", pmu_accessory_present() ? "true" : "false"); +#endif +#ifdef HAVE_MIKEY_REMOTE + /* r4/r5 are live register reads to help characterize the + * remote-ID behavior across units (it varies; see + * mikey-6g.c). r4 bit6 = ID bit, r5 = event register. */ + _DEBUG_PRINTF("mikey remote ctrl: %s r4=%02x r5=%02x", + mikey_present() ? "ok" : "--", + mikey_read(4), mikey_read(5)); #endif line++; _DEBUG_PRINTF("ADC:"); diff --git a/firmware/target/arm/s5l8702/ipod6g/audio-6g.c b/firmware/target/arm/s5l8702/ipod6g/audio-6g.c index 9775325cdc..244502f0e0 100644 --- a/firmware/target/arm/s5l8702/ipod6g/audio-6g.c +++ b/firmware/target/arm/s5l8702/ipod6g/audio-6g.c @@ -23,34 +23,10 @@ #include "sound.h" #include "pmu-target.h" #include "i2c-s5l8702.h" +#include "mikey-target.h" extern int rec_hw_ver; -/* Mikey is the internal controller for jack microphone and/or - * remote accessories. - * TODO: - * - move to mikey-6g.c - * - detect jack accessory - * - support for remote buttons - */ -unsigned char mikey_read(int address) -{ - unsigned char val; - i2c_read(0, 0x72, address, 1, &val); - return val; -} - -int mikey_write(int address, unsigned char val) -{ - return i2c_write(0, 0x72, address, 1, &val); -} - -void mikey_reset(void) -{ - mikey_write(0, 5); - mikey_write(1, 0x80); -} - #if INPUT_SRC_CAPS != 0 #ifdef HAVE_RECORDING void audio_enable_mic(bool enable) @@ -60,12 +36,12 @@ void audio_enable_mic(bool enable) if (enable) { - mikey_write(0, 7); /* raise voltage to polarize microphone */ + mikey_set_mic_capture(true); /* polarize mic, pause the remote */ GPIOCMD = 0xe070f; /* enable preamp */ } else { - mikey_reset(); /* microphone line voltage = 0 */ + mikey_set_mic_capture(false); GPIOCMD = 0xe070e; /* disable preamp */ } } diff --git a/firmware/target/arm/s5l8702/ipod6g/mikey-6g.c b/firmware/target/arm/s5l8702/ipod6g/mikey-6g.c new file mode 100644 index 0000000000..8165e51469 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/mikey-6g.c @@ -0,0 +1,342 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2026 by Hemant Kumar + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +/* "Mikey" is the internal controller for the headphone jack microphone and + * the inline earphone remote (I2C bus 0, address 0x72). + * + * Register protocol (reverse engineered on-device, 2026-07): + * reg0 = 0x2f remote-reporting mode. The low 3 bits are the mic bias + * level (7, same as the recording path uses); 0x28 arms + * the button detection engine. The chip resets on jack + * removal (all registers read zero afterwards), so the + * mode is re-armed on insertion. + * reg4 & 0x40 identified-remote bit, set after the chip hears an + * Apple remote's ID chirp. UNIT-DEPENDENT: reliably + * latched on one 6G, never observed on two other + * 120GB units, so nothing is gated on it; it is only + * shown in the hardware debug screen. + * reg4 & 0x05 center (play/pause) held; a level signal. Can + * flicker while the mode is (re)armed, hence the + * "armed" guard below. + * reg5 volume edge events, each readable for ~100ms: + * 0x04 vol+ press, 0x08 vol+ release, + * 0x01 vol- press, 0x02 vol- release. + * 0x30 is the accessory-ID event (no button meaning). + * The remote itself signals volume as static DC loads on the mic line and + * identifies via a one-time ultrasonic chirp (the same scheme David Carne + * documented for the shuffle 3G remote); Mikey does that level and chirp + * detection in hardware and reports the results as above. */ + +#include "system.h" +#include "cpu.h" +#include "kernel.h" +#include "thread.h" +#include "button.h" +#include "i2c-s5l8702.h" +#include "mikey-target.h" + +#define MIKEY_ADDR 0x72 +#define MIKEY_REG_MODE 0 /* mic bias + detection-engine mode */ +#define MIKEY_REG_BTN 4 /* center-button level */ +#define MIKEY_REG_EVT 5 /* volume press/release edge events */ + +#define MIKEY_MODE_OFF 0x05 /* reset default, mic line unpowered */ +#define MIKEY_MODE_MIC 0x07 /* mic bias raised, for recording */ +#define MIKEY_MODE_REMOTE 0x2f /* mic bias + button detection */ + +#define MIKEY_ID_FLAG 0x40 /* identified-remote latch in reg4 */ +#define MIKEY_BTN_CENTER 0x05 +#define MIKEY_EVT_VOLUP_DN 0x04 +#define MIKEY_EVT_VOLUP_UP 0x08 +#define MIKEY_EVT_VOLDN_DN 0x01 +#define MIKEY_EVT_VOLDN_UP 0x02 + +/* A press+release pair landing in a single poll is stretched over this + * many polls, so the button driver's debounce (two identical consecutive + * reads at the 10ms button tick) still accepts the click. */ +#define MIKEY_CLICK_POLLS 2 + +/* Center-button handling: click-only. reg4's rising edge is always + * immediate, but after ~5s of button inactivity the chip's event engine + * naps and reports the release (reg4 fall, reg6 events) ~1.8s late, so + * the press duration is unreliable. Every press is therefore reported + * as a fixed-length click at the rise; there is no hold/long-press. A + * release must be seen for several consecutive polls before a new rise + * counts, so bounce or an I2C NAK can't double-fire a click. */ +#define MIKEY_CENTER_PULSE_POLLS 3 /* reported click length, 60ms */ +#define MIKEY_CENTER_OFF_POLLS 3 /* release debounce, 60ms */ + +unsigned char mikey_read(int address) +{ + /* default to "no press, no events" if the transfer fails: the chip + * NAKs while it resets itself around jack removal/insertion */ + unsigned char val = 0; + i2c_read(0, MIKEY_ADDR, address, 1, &val); + return val; +} + +int mikey_write(int address, unsigned char val) +{ + return i2c_write(0, MIKEY_ADDR, address, 1, &val); +} + +void mikey_reset(void) +{ + mikey_write(MIKEY_REG_MODE, MIKEY_MODE_OFF); + mikey_write(1, 0x80); +} + +static int mikey_btn = BUTTON_NONE; +static volatile bool mic_active = false; +static bool mikey_detected = false; +static long mikey_stack[DEFAULT_STACK_SIZE/2/sizeof(long)]; + +bool mikey_present(void) +{ + return mikey_detected; +} + +/* The recording path owns the chip while the jack mic is enabled; the + * polling thread backs off and re-arms the remote mode afterwards. */ +void mikey_set_mic_capture(bool enable) +{ + mic_active = enable; + if (enable) + mikey_write(MIKEY_REG_MODE, MIKEY_MODE_MIC); + else + mikey_reset(); +} + +/* Decode one volume button's press/release edge events into a held state. + * A real hold only produces the two edges, so between them *held carries + * the state; *click stretches a same-poll press+release (see above). */ +static void mikey_decode_vol(unsigned char evt, unsigned char dn, + unsigned char up, bool *held, int *click) +{ + if (*click > 0 && --*click == 0) + *held = false; + + if (evt & dn) + { + *held = true; + *click = (evt & up) ? MIKEY_CLICK_POLLS : 0; + } + else if ((evt & up) && *click == 0) + *held = false; +} + +/* Phantom-load failsafe: some passive cables present a DC load on the + * mic line that Mikey decodes as a volume button, producing a press + * edge the moment the detection engine arms and never a release (seen + * in the wild as volume ramping to zero on its own). A press edge this + * early cannot be a human, so it suppresses that button until a + * release edge proves a real remote is attached. This is the + * edge-event analog of the center button's "armed" guard, which + * already keeps mic-less TRS plugs (a permanent mic-line short) from + * reading as a stuck center button. */ +#define MIKEY_ARM_WINDOW_POLLS 10 /* 200ms after (re)arming */ + +/* Per-poll decode state, kept apart from the thread's power/mode + * bookkeeping so the register-to-button logic is a pure function of + * (state, reg4, reg5). */ +struct mikey_decode { + bool armed; /* need a released reading before first press */ + bool vol_up, vol_dn; + int up_click, dn_click; + int arm_window; /* polls left of phantom-press suppression window */ + bool up_suppressed, dn_suppressed; + bool center_down; /* debounced "press already reported" */ + int center_off; /* consecutive released polls */ + int center_pulse; /* click pulse countdown */ +}; + +static void mikey_decode_reset(struct mikey_decode *d) +{ + d->armed = false; + d->vol_up = d->vol_dn = false; + d->up_click = d->dn_click = 0; + d->arm_window = MIKEY_ARM_WINDOW_POLLS; + d->up_suppressed = d->dn_suppressed = false; + d->center_down = false; + d->center_off = MIKEY_CENTER_OFF_POLLS; + d->center_pulse = 0; +} + +/* Apply the phantom-load failsafe to one volume button's edge bits: + * a press edge (without a same-poll release) inside the arm window + * marks the button suppressed; suppressed press edges are stripped + * until a release edge lifts the suppression. */ +static unsigned char mikey_vol_gate(struct mikey_decode *d, unsigned char evt, + unsigned char dn, unsigned char up, + bool *suppressed) +{ + if (evt & up) + *suppressed = false; + else if ((evt & dn) && d->arm_window > 0) + *suppressed = true; + + if (*suppressed) + evt &= ~dn; + return evt; +} + +/* Decode one poll's register pair into a button mask. Volume is NOT + * gated on the reg4 identified-remote bit: that bit proved to be + * unit-dependent (reliably latched on one 6G, never seen on two other + * 120GB units where gating on it broke volume entirely), so it is + * only surfaced in the hardware debug screen for now. */ +static int mikey_decode_poll(struct mikey_decode *d, + unsigned char btn, unsigned char evt) +{ + evt = mikey_vol_gate(d, evt, MIKEY_EVT_VOLUP_DN, MIKEY_EVT_VOLUP_UP, + &d->up_suppressed); + evt = mikey_vol_gate(d, evt, MIKEY_EVT_VOLDN_DN, MIKEY_EVT_VOLDN_UP, + &d->dn_suppressed); + if (d->arm_window > 0) + d->arm_window--; + + mikey_decode_vol(evt, MIKEY_EVT_VOLUP_DN, MIKEY_EVT_VOLUP_UP, + &d->vol_up, &d->up_click); + mikey_decode_vol(evt, MIKEY_EVT_VOLDN_DN, MIKEY_EVT_VOLDN_UP, + &d->vol_dn, &d->dn_click); + + /* reg4 can read pressed while the mode settles; don't report the + * center button until it has been seen released once */ + bool raw = (btn & MIKEY_BTN_CENTER) != 0; + if (!raw) + d->armed = true; + + /* click-only: emit a fixed pulse at each debounced rise */ + if (raw && d->armed) + { + if (!d->center_down && d->center_off >= MIKEY_CENTER_OFF_POLLS) + d->center_pulse = MIKEY_CENTER_PULSE_POLLS; + d->center_down = true; + d->center_off = 0; + } + else + { + if (d->center_off < MIKEY_CENTER_OFF_POLLS) + d->center_off++; + if (d->center_off >= MIKEY_CENTER_OFF_POLLS) + d->center_down = false; + } + + bool center = false; + if (d->center_pulse > 0) + { + center = true; + d->center_pulse--; + } + + /* All buttons report as multimedia keys: handled globally by + * default_event_handler on every screen, so volume and play/pause + * work in menus, WPS and plugins alike, matching the OF. */ + return (center ? BUTTON_MULTIMEDIA_PLAYPAUSE : 0) + | (d->vol_up ? BUTTON_MULTIMEDIA_VOLUME_UP : 0) + | (d->vol_dn ? BUTTON_MULTIMEDIA_VOLUME_DOWN : 0); +} + +/* The chip only ACKs the bus while something occupies the jack, so + * presence can't be probed at boot; it is probed here whenever the + * jack is occupied. A few insertion transients may NAK before the + * first ACK; a model without the chip (2007) NAKs forever, so after + * the retry budget the poll backs way off to stay out of the way. */ +#define MIKEY_PROBE_TRIES 25 /* ~2.5s of HZ/10 retries */ + +static void mikey_thread(void) +{ + bool powered = false; + int naks = 0; + struct mikey_decode decode; + + mikey_decode_reset(&decode); + + while (1) + { + if (!headphones_inserted() || mic_active) + { + if (powered && !mic_active) + mikey_reset(); + powered = false; + naks = 0; + mikey_decode_reset(&decode); + mikey_btn = BUTTON_NONE; + sleep(HZ/2); /* nothing to poll, check back at leisure */ + continue; + } + + /* Re-arm when the mode readback disagrees (first pass, chip reset + * on jack removal, or the recording path rewrote it). Arming can + * flicker reg4 and emits a spurious accessory-ID event, so drop + * the state and settle for one poll. */ + unsigned char mode; + if (i2c_read(0, MIKEY_ADDR, MIKEY_REG_MODE, 1, &mode) != 0) + { + /* no ACK: chip resetting around insertion, or not present */ + mikey_decode_reset(&decode); + mikey_btn = BUTTON_NONE; + if (naks < MIKEY_PROBE_TRIES) + { + naks++; + sleep(HZ/10); + } + else + sleep(HZ*2); + continue; + } + naks = 0; + powered = true; + mikey_detected = true; /* latched for the hw debug screen */ + + if (mode != MIKEY_MODE_REMOTE) + { + mikey_write(MIKEY_REG_MODE, MIKEY_MODE_REMOTE); + mikey_decode_reset(&decode); + mikey_btn = BUTTON_NONE; + sleep(HZ/50); + continue; + } + + unsigned char evt = mikey_read(MIKEY_REG_EVT); + unsigned char btn = mikey_read(MIKEY_REG_BTN); + mikey_btn = mikey_decode_poll(&decode, btn, evt); + + /* volume edge events stay readable for ~100ms; 20ms leaves a + * comfortable margin against scheduling jitter */ + sleep(HZ/50); + } +} + +int mikey_button_read(void) +{ + return mikey_btn; +} + +void mikey_init(void) +{ + /* Presence can't be probed here: the chip only ACKs while the + * jack is occupied (booting without headphones must not disable + * the remote). The thread probes on insertion instead and backs + * off on models without the chip (2007). */ + mikey_reset(); + create_thread(mikey_thread, mikey_stack, sizeof(mikey_stack), 0, + "mikey" IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU)); +} diff --git a/firmware/target/arm/s5l8702/ipod6g/mikey-target.h b/firmware/target/arm/s5l8702/ipod6g/mikey-target.h new file mode 100644 index 0000000000..888c14cde6 --- /dev/null +++ b/firmware/target/arm/s5l8702/ipod6g/mikey-target.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2026 by Hemant Kumar + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __MIKEY_TARGET_H__ +#define __MIKEY_TARGET_H__ + +/* "Mikey" is the internal controller for the headphone jack microphone + * and inline remote (I2C bus 0, address 0x72). */ + +/* Low-level register access. */ +unsigned char mikey_read(int address); +int mikey_write(int address, unsigned char val); +void mikey_reset(void); + +/* Recording-path handover: while the jack mic is enabled the remote + * polling backs off and Mikey stays in plain mic-bias mode. */ +void mikey_set_mic_capture(bool enable); + +/* Inline-remote button support. mikey_init() starts a polling thread; + * mikey_button_read() returns the current button mask (multimedia key + * codes, handled globally by default_event_handler: PLAYPAUSE for the + * center click, VOLUME_UP/DOWN for volume; BUTTON_NONE if nothing + * pressed) and is safe to call from the button tick. */ +void mikey_init(void); +int mikey_button_read(void); + +/* True once the controller has ever ACKed the bus (it only responds + * while the jack is occupied, so this latches on first insertion; + * stays false on the early 2007 models, which lack the chip). Shown + * in the hardware debug screen. */ +bool mikey_present(void); + +#endif /* __MIKEY_TARGET_H__ */ diff --git a/manual/rockbox_interface/main.tex b/manual/rockbox_interface/main.tex index 5b5c418792..594e261401 100644 --- a/manual/rockbox_interface/main.tex +++ b/manual/rockbox_interface/main.tex @@ -176,6 +176,14 @@ detail in the following paragraph. \ButtonHold{} is off before trying to use your player. } % + \opt{ipod6g}{ + Earphones with an Apple-compatible inline remote are supported (not on + the 2007 model). A click of the remote's center button toggles + play/pause on any screen, even while the \ButtonHold{} switch is on; + the center button has no long-press function. The remote's volume + buttons adjust the \dap{}'s volume everywhere. + } + % \opt{ipod3g}{ The main controls on the \dap{} are a slightly indented touch wheel with a flat round button in the center, and four buttons in a row above the -- rockbox-cvs mailing list [email protected] https://lists.haxx.se/mailman/listinfo/rockbox-cvs