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 <noreply@anthropic.com>
Change-Id: I870038fb6a21a9c26b025e3df7c220fd925f49f6
This commit is contained in:
Marcin Bukat 2026-09-25 15:43:35 +02:00
parent f7655e226c
commit a35f344eef
5 changed files with 75 additions and 9 deletions

View file

@ -9284,15 +9284,15 @@
user: core
<source>
*: none
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Bass Cutoff"
</source>
<dest>
*: none
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Bass Cutoff"
</dest>
<voice>
*: none
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Bass Cutoff"
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Bass Cutoff"
</voice>
</phrase>
<phrase>
@ -9301,15 +9301,15 @@
user: core
<source>
*: none
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Treble Cutoff"
</source>
<dest>
*: none
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Treble Cutoff"
</dest>
<voice>
*: none
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100: "Treble Cutoff"
gigabeatfx,ipod6g,ipodnano3g,ipodnano4g,ipodvideo,mpiohd200,mpiohd300,mrobe100,samsungypcp3: "Treble Cutoff"
</voice>
</phrase>
<phrase>

View file

@ -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

View file

@ -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. */

View file

@ -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 */

View file

@ -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);
}