From a35f344eef1a46948b9f329b30b285bb7340dfbc Mon Sep 17 00:00:00 2001 From: Marcin Bukat Date: Fri, 25 Sep 2026 15:43:35 +0200 Subject: [PATCH] YP-CP3: WM8750 audio The YP-CP3 uses Wolfson WM8750. Headphones are wired on OUT2 and a headphone amplifier enabled by GPIO F2, active high - all RE from the original firmware. The original firmware runs the codec as I2S master in its 12 MHz "USB mode", fed a fixed 12 MHz MCLK, which puts 44.1 kHz at 44.118. Rockbox instead makes the rk27xx the master and clocks the codec from the codec PLL at exactly 256 fs (CODEC_SLAVE, as every other rk27xx target with an external codec), so the codec's CLOCKING register is its normal-mode 256 fs setting at every rate. 96 kHz is left out: the WM8750 cannot take it at 256 fs. - config: HAVE_WM8750, CODEC_SLAVE, rates 8-48 kHz; the WM8750 has hardware tone controls, so HAVE_SW_TONE_CONTROLS goes - ypcp3/wmcodec-ypcp3.c: register writes over the rk27xx I2C driver - wm8751.c: on the YP-CP3, power on and drive OUT2 instead of OUT1, set the volume there, and switch the amplifier with the outputs - english.lang: the YP-CP3 gets the bass/treble cutoff settings the WM8750 brings Tested on a YP-CP3: playback at 44.1 and 48 kHz on headphones, pitch and volume correct. Co-Authored-By: Claude Opus 5.5 Change-Id: I870038fb6a21a9c26b025e3df7c220fd925f49f6 --- apps/lang/english.lang | 12 +++---- firmware/SOURCES | 3 ++ firmware/drivers/audio/wm8751.c | 19 ++++++++++ firmware/export/config/samsungypcp3.h | 14 ++++++-- .../target/arm/rk27xx/ypcp3/wmcodec-ypcp3.c | 36 +++++++++++++++++++ 5 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 firmware/target/arm/rk27xx/ypcp3/wmcodec-ypcp3.c diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 4c2f0d58b0..112c4d9257 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -9284,15 +9284,15 @@ user: core *: none - gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff" + gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Bass Cutoff" *: none - gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff" + gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Bass Cutoff" *: none - gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff" + gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Bass Cutoff" @@ -9301,15 +9301,15 @@ user: core *: none - gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff" + gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Treble Cutoff" *: none - gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff" + gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Treble Cutoff" *: none - gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff" + gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Treble Cutoff" diff --git a/firmware/SOURCES b/firmware/SOURCES index 2c8ff4bdbe..ab8660d653 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1736,6 +1736,9 @@ target/arm/rk27xx/ypcp3/button-ypcp3.c target/arm/rk27xx/ypcp3/powermgmt-ypcp3.c target/arm/rk27xx/ypcp3/power-ypcp3.c target/arm/rk27xx/ypcp3/lcd-ypcp3.c +#ifndef BOOTLOADER +target/arm/rk27xx/ypcp3/wmcodec-ypcp3.c +#endif #endif #if CONFIG_CPU == JZ4732 diff --git a/firmware/drivers/audio/wm8751.c b/firmware/drivers/audio/wm8751.c index 9a688f6589..dcd1583fb7 100644 --- a/firmware/drivers/audio/wm8751.c +++ b/firmware/drivers/audio/wm8751.c @@ -187,6 +187,10 @@ void audiohw_preinit(void) and_l(~(1<<5), &GPIO1_OUT); or_l((1<<5), &GPIO1_ENABLE); or_l((1<<5), &GPIO1_FUNCTION); +#elif defined(SAMSUNG_YPCP3) + /* headphone amplifier enable, GPIO F2 active high: off for now */ + GPIO_PFDR &= ~(1<<2); + GPIO_PFCON |= (1<<2); #endif /* @@ -233,6 +237,9 @@ void audiohw_postinit(void) /* headphones + line-out */ wmcodec_set_bits(PWRMGMT2, PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1 | PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2); +#elif defined(SAMSUNG_YPCP3) + /* headphones, on OUT2 */ + wmcodec_set_bits(PWRMGMT2, PWRMGMT2_LOUT2 | PWRMGMT2_ROUT2); #else /* headphones */ wmcodec_set_bits(PWRMGMT2, PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1); @@ -286,6 +293,8 @@ void audiohw_postinit(void) or_l((1<<25), &GPIO1_OUT); #elif defined(MPIO_HD300) or_l((1<<5), &GPIO1_OUT); +#elif defined(SAMSUNG_YPCP3) + GPIO_PFDR |= (1<<2); #endif } @@ -293,10 +302,18 @@ void audiohw_set_volume(int vol_l, int vol_r) { vol_l = vol_tenthdb2hw(vol_l); vol_r = vol_tenthdb2hw(vol_r); +#if defined(SAMSUNG_YPCP3) + /* headphones on OUT2 */ + wmcodec_set_masked(LOUT2, LOUT2_LOUT2VOL(vol_l), + LOUT2_LOUT2VOL_MASK); + wmcodec_set_masked(ROUT2, ROUT2_RO2VU | ROUT2_ROUT2VOL(vol_r), + ROUT2_ROUT2VOL_MASK); +#else wmcodec_set_masked(LOUT1, LOUT1_LOUT1VOL(vol_l), LOUT1_LOUT1VOL_MASK); wmcodec_set_masked(ROUT1, ROUT1_RO1VU | ROUT1_ROUT1VOL(vol_r), ROUT1_ROUT1VOL_MASK); +#endif } #ifdef AUDIOHW_HAVE_LINEOUT @@ -353,6 +370,8 @@ void audiohw_close(void) and_l(~(1<<25), &GPIO1_OUT); #elif defined(MPIO_HD300) and_l(~(1<<5), &GPIO1_OUT); +#elif defined(SAMSUNG_YPCP3) + GPIO_PFDR &= ~(1<<2); #endif /* 2. Disable all output buffers. */ diff --git a/firmware/export/config/samsungypcp3.h b/firmware/export/config/samsungypcp3.h index d716ab2aca..61f0925b13 100644 --- a/firmware/export/config/samsungypcp3.h +++ b/firmware/export/config/samsungypcp3.h @@ -44,7 +44,6 @@ #define CONFIG_STORAGE (STORAGE_SD | STORAGE_NAND) #define CONFIG_NAND NAND_RK27XX -#define HAVE_SW_TONE_CONTROLS /* commented for now */ /* #define HAVE_HOTSWAP */ @@ -93,8 +92,17 @@ #define CONFIG_LCD LCD_SPFD5420A -/* Define the type of audio codec */ -#define HAVE_RK27XX_CODEC +/* Wolfson WM8750 on I2C, headphones on its OUT2. The codec is the I2S + * slave: the rk27xx drives the bus and clocks the codec from its codec PLL + * at 256 fs for every rate (pcm-rk27xx.c), so the codec's CLOCKING register + * is its normal-mode 256 fs setting throughout. The original firmware runs + * the codec as master off a fixed 12 MHz instead, in USB mode, which puts + * 44.1 kHz at 44.118. */ +#define HAVE_WM8750 +#define CODEC_SLAVE +#define CODEC_SRCTRL_11025HZ 0 +#define CODEC_SRCTRL_22050HZ 0 +#define CODEC_SRCTRL_44100HZ 0 /* Seiko S-35390A real-time clock on I2C, left in 12-hour mode by the * original firmware */ diff --git a/firmware/target/arm/rk27xx/ypcp3/wmcodec-ypcp3.c b/firmware/target/arm/rk27xx/ypcp3/wmcodec-ypcp3.c new file mode 100644 index 0000000000..16ba0bf0f2 --- /dev/null +++ b/firmware/target/arm/rk27xx/ypcp3/wmcodec-ypcp3.c @@ -0,0 +1,36 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2026 by Marcin Bukat + * + * 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. + * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "i2c-rk27xx.h" +#include "wmcodec.h" + +/* The WM8750's control interface in 2-wire mode, CSB low */ +#define WM8750_I2C_ADDR 0x34 + +/* A register write is two bytes: the 7-bit register number over bit 8 of + * the value, then the value's low byte. */ +void wmcodec_write(int reg, int data) +{ + unsigned char lo = data & 0xff; + + i2c_write(WM8750_I2C_ADDR, (reg << 1) | ((data >> 8) & 1), 1, &lo); +}