mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
ipodnano3g: power, RTC, backlight, battery and audio
Fills in the stubs the Nano 3G port was left with, taking each from how the original firmware drives the same hardware. Testing evidence: firmware/target/arm/s5l8702/ipodnano3g/TESTING.md. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Change-Id: Iea5314769502f5941a176600869591756c5e3233
This commit is contained in:
parent
e4c010be98
commit
76f8925d23
18 changed files with 286 additions and 129 deletions
|
|
@ -426,7 +426,7 @@ static const char graphic_numeric[] = "graphic,numeric";
|
|||
# define DEFAULT_REC_MIC_GAIN 0
|
||||
# define DEFAULT_REC_LEFT_GAIN 0
|
||||
# define DEFAULT_REC_RIGHT_GAIN 0
|
||||
# elif defined(HAVE_WM8975)
|
||||
# elif defined(HAVE_WM8975) || defined(HAVE_WM1870)
|
||||
# define DEFAULT_REC_MIC_GAIN 16
|
||||
# define DEFAULT_REC_LEFT_GAIN 0
|
||||
# define DEFAULT_REC_RIGHT_GAIN 0
|
||||
|
|
|
|||
|
|
@ -575,7 +575,7 @@ drivers/audio/wm8740.c
|
|||
drivers/audio/wm8751.c
|
||||
#elif defined(HAVE_WM8978)
|
||||
drivers/audio/wm8978.c
|
||||
#elif defined(HAVE_WM8975)
|
||||
#elif defined(HAVE_WM8975) || defined(HAVE_WM1870)
|
||||
drivers/audio/wm8975.c
|
||||
#elif defined(HAVE_WM8985)
|
||||
drivers/audio/wm8985.c
|
||||
|
|
@ -1647,7 +1647,7 @@ target/arm/s5l8702/ipodnano3g/serial-nano3g.c
|
|||
#endif
|
||||
#ifndef BOOTLOADER
|
||||
target/arm/s5l8702/ipodnano3g/audio-nano3g.c
|
||||
target/arm/s5l8702/ipodnano3g/cscodec-nano3g.c // TODO: -> wmcodec-nano3g.c
|
||||
target/arm/s5l8702/ipodnano3g/wmcodec-nano3g.c
|
||||
#endif
|
||||
#endif /* IPOD_NANO3G */
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* Wolfson WM8975 codec driver. It also covers the WM1870 fitted to the iPod
|
||||
* Nano 3G (HAVE_WM1870), which answers the same registers and adds a few
|
||||
* above them; the differences are behind that define.
|
||||
*/
|
||||
|
||||
#include "logf.h"
|
||||
#include "system.h"
|
||||
#include "kernel.h"
|
||||
|
|
@ -34,6 +40,9 @@
|
|||
|
||||
#include "wmcodec.h"
|
||||
#include "audiohw.h"
|
||||
#ifdef HAVE_WM1870
|
||||
#include "pcm_sampr.h"
|
||||
#endif
|
||||
|
||||
static unsigned short wm8975_regs[WM8975_NUM_REGISTERS] =
|
||||
{
|
||||
|
|
@ -126,7 +135,35 @@ void audiohw_postinit(void)
|
|||
wm8975_write(PWRMGMT1, wm8975_regs[PWRMGMT1]);
|
||||
audiohw_mute(false);
|
||||
}
|
||||
#else /* !IPOD_NANO2G */
|
||||
#elif defined(HAVE_WM1870)
|
||||
/* The WM1870, set up as the original firmware does it. It answers the
|
||||
* WM8975's registers and a few above them. */
|
||||
|
||||
void audiohw_preinit(void)
|
||||
{
|
||||
wm8975_write(RESET, RESET_RESET);
|
||||
|
||||
wm8975_write(PWRMGMT1, PWRMGMT1_VMIDSEL_50K | PWRMGMT1_VREF);
|
||||
wm8975_write(AINTFCE, AINTFCE_MS | AINTFCE_LRP_I2S_RLO
|
||||
| AINTFCE_IWL_16BIT | AINTFCE_FORMAT_I2S);
|
||||
wm8975_write(PWRMGMT2, PWRMGMT2_DACL | PWRMGMT2_DACR
|
||||
| PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1);
|
||||
|
||||
wm8975_write(LOUTMIX1, LOUTMIX1_LD2LO | LOUTMIX1_LI2LOVOL(2)
|
||||
| LOUTMIX1_LMIXSEL_LADCIN);
|
||||
wm8975_write(ROUTMIX2, ROUTMIX2_RD2RO | ROUTMIX2_RI2ROVOL(2));
|
||||
wm8975_write(ROUTMIX1, ROUTMIX1_RMIXSEL_RADCIN);
|
||||
|
||||
wmcodec_write(WM1870_R67, 0x008);
|
||||
wm8975_write(ADDCTRL2, ADDCTRL2_OUT3SW_MONOOUT | ADDCTRL2_LRCM);
|
||||
wm8975_write(ADDCTRL3, ADDCTRL3_VROI);
|
||||
}
|
||||
|
||||
void audiohw_postinit(void)
|
||||
{
|
||||
audiohw_mute(false);
|
||||
}
|
||||
#else /* !IPOD_NANO2G && !HAVE_WM1870 */
|
||||
void audiohw_preinit(void)
|
||||
{
|
||||
/* POWER UP SEQUENCE */
|
||||
|
|
@ -240,7 +277,15 @@ void audiohw_close(void)
|
|||
/* Note: Disable output before calling this function */
|
||||
void audiohw_set_frequency(int fsel)
|
||||
{
|
||||
#ifdef HAVE_WM1870
|
||||
/* The rate codes depend on the MCLK, so the target supplies them */
|
||||
unsigned short sampctrl = wmcodec_sampctrl(hw_freq_sampr[fsel]);
|
||||
|
||||
if (sampctrl)
|
||||
wm8975_write(SAMPCTRL, sampctrl);
|
||||
#else
|
||||
(void)fsel;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ struct sound_settings_info
|
|||
#include "wm8751.h"
|
||||
#elif defined(HAVE_WM8978)
|
||||
#include "wm8978.h"
|
||||
#elif defined(HAVE_WM8975)
|
||||
#elif defined(HAVE_WM8975) || defined(HAVE_WM1870)
|
||||
#include "wm8975.h"
|
||||
#elif defined(HAVE_WM8985)
|
||||
#include "wm8985.h"
|
||||
|
|
|
|||
|
|
@ -138,22 +138,17 @@
|
|||
#define HAVE_MORSE_INPUT
|
||||
|
||||
/* define this if you have a real-time clock */
|
||||
// TODO
|
||||
//#define CONFIG_RTC RTC_NANO3G
|
||||
#define CONFIG_RTC 0
|
||||
#define CONFIG_RTC RTC_NANO3G
|
||||
|
||||
/* Define if the device can wake from an RTC alarm */
|
||||
//#define HAVE_RTC_ALARM
|
||||
|
||||
#define CONFIG_LCD LCD_IPOD6GNANO3G4G
|
||||
|
||||
// TODO
|
||||
#if 0
|
||||
/* Define the type of audio codec */
|
||||
#define HAVE_WM8975
|
||||
#endif
|
||||
// XXX: dummy for preliminary build, WRONG CODEC!!!
|
||||
#define HAVE_CS42L55
|
||||
/* Define the type of audio codec. The Nano 3G has a Wolfson WM1870 at I2C
|
||||
* address 0x34, which the original firmware drives as a WM8975 plus a few
|
||||
* registers above the WM8975's range, so wm8975.c covers it. */
|
||||
#define HAVE_WM1870
|
||||
|
||||
#define HAVE_PCM_DMA_ADDRESS
|
||||
|
||||
|
|
|
|||
|
|
@ -313,4 +313,7 @@ void audiohw_enable_lineout(bool enable);
|
|||
#define WM8975_88200HZ 0x7f
|
||||
#define WM8975_96000HZ 0x5d
|
||||
|
||||
/* The WM1870 (HAVE_WM1870) answers these registers and a few above them */
|
||||
#define WM1870_R67 0x43
|
||||
|
||||
#endif /* _WM8975_H */
|
||||
|
|
|
|||
|
|
@ -20,3 +20,9 @@
|
|||
|
||||
void wmcodec_write(int reg, int data);
|
||||
|
||||
#ifdef HAVE_WM1870
|
||||
/* SAMPCTRL for a sample rate, which depends on the MCLK the target feeds
|
||||
* the codec. Zero if the target cannot make that rate. */
|
||||
unsigned short wmcodec_sampctrl(unsigned long rate);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -425,8 +425,13 @@ bool headphones_inserted(void)
|
|||
#if CONFIG_CPU==S5L8701
|
||||
return ((PDAT14 & (1 << 5)) != 0);
|
||||
#elif CONFIG_CPU==S5L8702 || CONFIG_CPU==S5L8720
|
||||
#ifdef IPOD_NANO3G
|
||||
/* GPIO 7.0, high with headphones in (measured) */
|
||||
return (PDAT(7) & (1 << 0)) != 0;
|
||||
#else
|
||||
return ((PDAT10 & (1 << 6)) != 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -21,3 +21,13 @@ Tested on the 4GB unit from DFU: host sees "Nano 3G NAND", 2113537 x
|
|||
2048-byte sectors, write protect on. `nandcheck.py collect` reads it in
|
||||
seven minutes on Linux and Windows, no ECC failures or timeouts, and the
|
||||
archive passes the host FTL suite.
|
||||
|
||||
## Power, RTC, backlight, battery, audio
|
||||
|
||||
Tested on the 4GB unit: 44.1 kHz WAV plays with elapsed time running;
|
||||
battery reads 4077 mV, charging true/false correctly with a cell below
|
||||
full; a time set in Rockbox survives a reboot; backlight and brightness
|
||||
work; "pause on headphone unplug" pauses on disconnect.
|
||||
|
||||
Not tested: sample rates other than 44.1 kHz, and the charge-complete
|
||||
status bits (never seen set).
|
||||
|
|
|
|||
|
|
@ -27,17 +27,51 @@
|
|||
#include "pmu-target.h"
|
||||
#include "kernel.h"
|
||||
|
||||
/* Only the battery input is known: the original firmware selects it with
|
||||
* 0x24 and converts with 2500 mV + raw * 2000 mV / 1023 (measured: 4062 mV
|
||||
* on battery, 4110 mV charging). The USB data and accessory inputs are not
|
||||
* identified yet and read as 0. */
|
||||
static const struct pmu_adc_channel adc_channels[] =
|
||||
{
|
||||
[ADC_BATTERY] =
|
||||
{
|
||||
.name = "Battery",
|
||||
.mux = 0x24,
|
||||
.samples = 4,
|
||||
.offset_mv = 2500,
|
||||
.span_mv = 2000,
|
||||
},
|
||||
[ADC_USBDATA] =
|
||||
{
|
||||
.name = "USB data",
|
||||
},
|
||||
[ADC_ACCESSORY] =
|
||||
{
|
||||
.name = "Accessory",
|
||||
},
|
||||
};
|
||||
|
||||
unsigned short adc_read_millivolts(int channel)
|
||||
{
|
||||
const struct pmu_adc_channel *ch = &adc_channels[channel];
|
||||
|
||||
if (!ch->samples)
|
||||
return 0;
|
||||
return pmu_adc_raw2mv(ch, pmu_read_adc(ch));
|
||||
}
|
||||
|
||||
/* Returns battery voltage [millivolts] */
|
||||
unsigned int adc_read_battery_voltage(void)
|
||||
{
|
||||
return 0;
|
||||
return adc_read_millivolts(ADC_BATTERY);
|
||||
}
|
||||
|
||||
/* API functions */
|
||||
unsigned short adc_read(int channel)
|
||||
{
|
||||
(void) channel;
|
||||
return 0;
|
||||
const struct pmu_adc_channel *ch = &adc_channels[channel];
|
||||
|
||||
return ch->samples ? pmu_read_adc(ch) : 0;
|
||||
}
|
||||
|
||||
int adc_read_accessory_resistor(void)
|
||||
|
|
@ -53,8 +87,7 @@ unsigned int adc_read_usbdata_voltage(bool dp)
|
|||
|
||||
const char *adc_name(int channel)
|
||||
{
|
||||
(void) channel;
|
||||
return "";
|
||||
return adc_channels[channel].name;
|
||||
}
|
||||
|
||||
void adc_init(void)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
#include "lcd-s5l8702.h"
|
||||
#endif
|
||||
|
||||
// TODO: test
|
||||
/* The original firmware writes the level to bits 0..4 as well */
|
||||
void backlight_hw_brightness(int brightness)
|
||||
{
|
||||
pmu_write(D1671_REG_LEDCTL,
|
||||
|
|
@ -43,8 +43,11 @@ void backlight_hw_on(void)
|
|||
if (!lcd_active())
|
||||
lcd_awake();
|
||||
#endif
|
||||
/* As the original firmware turns it on: bits 7 and 6 set,
|
||||
* bit 5 clear. Bit 6's meaning is unknown. */
|
||||
pmu_write(D1671_REG_LEDCTL,
|
||||
(pmu_read(D1671_REG_LEDCTL) | D1671_LEDCTL_ENABLE));
|
||||
(pmu_read(D1671_REG_LEDCTL) & ~0x20)
|
||||
| D1671_LEDCTL_ENABLE | D1671_LEDCTL_UNKNOWN);
|
||||
}
|
||||
|
||||
void backlight_hw_off(void)
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* S5L8702-specific code for Cirrus codecs
|
||||
*
|
||||
* Copyright (c) 2010 Michael Sparmann
|
||||
*
|
||||
* 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 "system.h"
|
||||
#include "audiohw.h"
|
||||
#include "i2c-s5l8702.h"
|
||||
#include "s5l87xx.h"
|
||||
#include "cscodec.h"
|
||||
|
||||
void audiohw_init(void)
|
||||
{
|
||||
#ifdef HAVE_CS42L55
|
||||
audiohw_preinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned char cscodec_read(int reg)
|
||||
{
|
||||
// TODO: not tested
|
||||
unsigned char data;
|
||||
i2c_read(0, 0x94, reg, 1, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void cscodec_write(int reg, unsigned char data)
|
||||
{
|
||||
// XXX: not tested
|
||||
i2c_write(0, 0x94, reg, 1, &data);
|
||||
}
|
||||
|
||||
void cscodec_power(bool state)
|
||||
{
|
||||
(void)state; //TODO: Figure out which LDO this is
|
||||
}
|
||||
|
||||
void cscodec_reset(bool state)
|
||||
{
|
||||
// XXX: not tested
|
||||
if (state) PDAT(3) &= ~8;
|
||||
else PDAT(3) |= 8;
|
||||
}
|
||||
|
||||
void cscodec_clock(bool state)
|
||||
{
|
||||
// XXX: not tested
|
||||
if (state) CLKCON3 &= ~0xffff;
|
||||
else CLKCON3 |= 0x8000;
|
||||
}
|
||||
|
|
@ -112,16 +112,26 @@ void pmu_set_cpu_voltage(bool high)
|
|||
#endif
|
||||
|
||||
#if (CONFIG_RTC == RTC_NANO3G)
|
||||
/* The RTC, as the original firmware uses it:
|
||||
* registers 0x40..0x45 hold seconds, minutes, hours, day, month and year
|
||||
* (since 2000), in binary. Bit 6 of the seconds register is set whenever
|
||||
* the time is written and masked off when it is read. The buffer holds the
|
||||
* six values in that order. */
|
||||
void pmu_read_rtc(unsigned char* buffer)
|
||||
{
|
||||
// TODO
|
||||
(void) buffer;
|
||||
pmu_read_multiple(D1671_REG_RTCSEC, 6, buffer);
|
||||
buffer[0] &= ~D1671_RTCSEC_SET;
|
||||
}
|
||||
|
||||
void pmu_write_rtc(unsigned char* buffer)
|
||||
{
|
||||
// TODO
|
||||
(void) buffer;
|
||||
int i;
|
||||
|
||||
/* One register at a time, seconds first, as the original firmware
|
||||
* does */
|
||||
for (i = 0; i < 6; i++)
|
||||
pmu_write(D1671_REG_RTCSEC + i,
|
||||
buffer[i] | (i == 0 ? D1671_RTCSEC_SET : 0));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -132,7 +142,10 @@ void pmu_set_usblimit(bool fast_charge)
|
|||
}
|
||||
|
||||
/*
|
||||
* ADC
|
||||
* ADC, as the original firmware drives it: write the channel's
|
||||
* input selection to register 0x30 with bit 3 set to start a conversion,
|
||||
* wait for bit 3 to clear, and read the 10-bit result from 0x32 (bits 9..2)
|
||||
* and 0x31 (bits 1..0). 0x30 idles at 0x20.
|
||||
*/
|
||||
static struct mutex pmu_adc_mutex;
|
||||
|
||||
|
|
@ -140,21 +153,31 @@ static struct mutex pmu_adc_mutex;
|
|||
unsigned short pmu_adc_raw2mv(
|
||||
const struct pmu_adc_channel *ch, unsigned short raw)
|
||||
{
|
||||
// TODO
|
||||
(void) ch;
|
||||
(void) raw;
|
||||
return 0;
|
||||
return ch->offset_mv + raw * ch->span_mv / 1023;
|
||||
}
|
||||
|
||||
/* returns raw value */
|
||||
/* returns raw value, averaged over ch->samples conversions */
|
||||
unsigned short pmu_read_adc(const struct pmu_adc_channel *ch)
|
||||
{
|
||||
// TODO
|
||||
int raw = 0;
|
||||
unsigned int sum = 0;
|
||||
int i, tries;
|
||||
|
||||
mutex_lock(&pmu_adc_mutex);
|
||||
(void) ch;
|
||||
for (i = 0; i < ch->samples; i++)
|
||||
{
|
||||
pmu_write(D1671_REG_ADCCTL, ch->mux | D1671_ADCCTL_START);
|
||||
for (tries = 0; tries < 20; tries++)
|
||||
{
|
||||
udelay(50);
|
||||
if (!(pmu_read(D1671_REG_ADCCTL) & D1671_ADCCTL_START))
|
||||
break;
|
||||
}
|
||||
sum += (pmu_read(D1671_REG_ADCHI) << 2)
|
||||
| (pmu_read(D1671_REG_ADCLO) & 3);
|
||||
}
|
||||
pmu_write(D1671_REG_ADCCTL, D1671_ADCCTL_IDLE);
|
||||
mutex_unlock(&pmu_adc_mutex);
|
||||
return raw;
|
||||
return ch->samples ? sum / ch->samples : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ enum d1671_regs {
|
|||
D1671_REG_SYSCTRLA = 0x08,
|
||||
D1671_REG_LEDCTL = 0x20,
|
||||
D1671_REG_CHCTL = 0x21, // TBC
|
||||
D1671_REG_ADCCTL = 0x30,
|
||||
D1671_REG_ADCLO = 0x31,
|
||||
D1671_REG_ADCHI = 0x32,
|
||||
D1671_REG_RTCSEC = 0x40, // TBC
|
||||
};
|
||||
|
||||
|
|
@ -87,13 +90,26 @@ enum d1671_reg_chctl {
|
|||
D1671_CHCTL_FASTCHRG = 0x01, /* 100/500mA USB limit */
|
||||
};
|
||||
|
||||
enum d1671_reg_rtcsec {
|
||||
/* set when the time is written, masked off when it is read */
|
||||
D1671_RTCSEC_SET = 0x40,
|
||||
};
|
||||
|
||||
enum d1671_reg_adcctl {
|
||||
D1671_ADCCTL_START = 0x08,
|
||||
D1671_ADCCTL_IDLE = 0x20, /* register's value at rest */
|
||||
};
|
||||
|
||||
/* GPIO for external PMU interrupt */
|
||||
#define GPIO_EINT_PMU 0x7b
|
||||
|
||||
struct pmu_adc_channel
|
||||
{
|
||||
const char *name;
|
||||
// TODO
|
||||
uint8_t mux; /* register 0x30 value selecting the input */
|
||||
uint8_t samples; /* conversions averaged, 0 = not readable */
|
||||
unsigned short offset_mv; /* millivolts at raw 0 */
|
||||
unsigned short span_mv; /* millivolts from raw 0 to raw 1023 */
|
||||
};
|
||||
|
||||
void pmu_preinit(void);
|
||||
|
|
|
|||
|
|
@ -58,9 +58,19 @@ unsigned int power_input_status(void)
|
|||
return status;
|
||||
}
|
||||
|
||||
/* As the original firmware decides it: the charger is
|
||||
* enabled (CHCTL bits 1..6), not suspended (SYSCTRLA bit 2), and not done:
|
||||
* STATUSB bits 1..2 clear. Measured: they read 0 while a 4.1 V cell
|
||||
* charges from USB, so they are taken as the charge-complete indication;
|
||||
* they have not been seen set. */
|
||||
bool charging_state(void)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
if (!(power_input_status() & POWER_INPUT_CHARGER))
|
||||
return false;
|
||||
if (!(pmu_read(D1671_REG_CHCTL) & 0x7e))
|
||||
return false;
|
||||
if (pmu_read(D1671_REG_SYSCTRLA) & 0x04)
|
||||
return false;
|
||||
return !(pmu_read(D1671_REG_STATUSB) & 0x06);
|
||||
}
|
||||
#endif /* CONFIG_CHARGING */
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
#include "pmu-target.h"
|
||||
#include "timefuncs.h"
|
||||
|
||||
/* The PMU keeps seconds, minutes, hours, day, month and year (since 2000)
|
||||
* in binary, and no weekday; see pmu_read_rtc() */
|
||||
|
||||
void rtc_init(void)
|
||||
{
|
||||
|
|
@ -31,20 +33,16 @@ void rtc_init(void)
|
|||
|
||||
int rtc_read_datetime(struct tm *tm)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char buf[7];
|
||||
unsigned char buf[6];
|
||||
|
||||
pmu_read_rtc(buf);
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
buf[i] = BCD2DEC(buf[i]);
|
||||
|
||||
tm->tm_sec = buf[0];
|
||||
tm->tm_min = buf[1];
|
||||
tm->tm_hour = buf[2];
|
||||
tm->tm_mday = buf[4];
|
||||
tm->tm_mon = buf[5] - 1;
|
||||
tm->tm_year = buf[6] + 100;
|
||||
tm->tm_mday = buf[3];
|
||||
tm->tm_mon = buf[4] - 1;
|
||||
tm->tm_year = buf[5] + 100;
|
||||
tm->tm_yday = 0; /* Not implemented for now */
|
||||
|
||||
set_day_of_week(tm);
|
||||
|
|
@ -53,19 +51,14 @@ int rtc_read_datetime(struct tm *tm)
|
|||
|
||||
int rtc_write_datetime(const struct tm *tm)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned char buf[7];
|
||||
unsigned char buf[6];
|
||||
|
||||
buf[0] = tm->tm_sec;
|
||||
buf[1] = tm->tm_min;
|
||||
buf[2] = tm->tm_hour;
|
||||
buf[3] = tm->tm_wday;
|
||||
buf[4] = tm->tm_mday;
|
||||
buf[5] = tm->tm_mon + 1;
|
||||
buf[6] = tm->tm_year - 100;
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
buf[i] = DEC2BCD(buf[i]);
|
||||
buf[3] = tm->tm_mday;
|
||||
buf[4] = tm->tm_mon + 1;
|
||||
buf[5] = tm->tm_year - 100;
|
||||
|
||||
pmu_write_rtc(buf);
|
||||
return 0;
|
||||
|
|
|
|||
65
firmware/target/arm/s5l8702/ipodnano3g/wmcodec-nano3g.c
Normal file
65
firmware/target/arm/s5l8702/ipodnano3g/wmcodec-nano3g.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
*
|
||||
* iPod Nano 3G code for the Wolfson codec (WM1870)
|
||||
*
|
||||
* Copyright (c) 2010 Michael Sparmann
|
||||
* Copyright (C) 2026 by Andrew Rice
|
||||
*
|
||||
* 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 "system.h"
|
||||
#include "audiohw.h"
|
||||
#include "i2c-s5l8702.h"
|
||||
#include "wmcodec.h"
|
||||
|
||||
/* The PCM driver sets the codec up (audiohw_preinit()) and controls its
|
||||
* MCLK */
|
||||
void audiohw_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* The codec runs from a 12 MHz MCLK here, so its rate codes are the USB
|
||||
* mode ones, with a BCLK divider, as the original firmware sets them. */
|
||||
unsigned short wmcodec_sampctrl(unsigned long rate)
|
||||
{
|
||||
static const struct
|
||||
{
|
||||
unsigned long rate;
|
||||
unsigned short sampctrl;
|
||||
} rates[] =
|
||||
{
|
||||
{ SAMPR_8, 0x10d }, { SAMPR_11, 0x133 }, { SAMPR_12, 0x111 },
|
||||
{ SAMPR_16, 0x115 }, { SAMPR_22, 0x137 }, { SAMPR_24, 0x139 },
|
||||
{ SAMPR_32, 0x119 }, { SAMPR_44, 0x123 }, { SAMPR_48, 0x081 },
|
||||
{ SAMPR_88, 0x0bf }, { SAMPR_96, 0x09d },
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAYLEN(rates); i++)
|
||||
if (rates[i].rate == rate)
|
||||
return rates[i].sampctrl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 7-bit register and 9-bit value, as two bytes to address 0x34 on I2C
|
||||
* bus 0. The codec does not acknowledge reads. */
|
||||
void wmcodec_write(int reg, int data)
|
||||
{
|
||||
unsigned char d = data & 0xff;
|
||||
|
||||
i2c_write(0, 0x34, (reg << 1) | ((data >> 8) & 1), 1, &d);
|
||||
}
|
||||
|
|
@ -80,6 +80,18 @@ static struct dmac_ch_cfg dma_play_ch_cfg = {
|
|||
#define CHUNK_MAX_BYTES (LLI_MAX_BYTES * 1)
|
||||
#define WATERMARK_BYTES (PCM_WATERMARK * 4)
|
||||
|
||||
/* I2S0 TX setup and start command. The Nano 3G uses the values Apple's
|
||||
* firmware writes for its WM1870, which is the I2S clock master; the
|
||||
* ipod6g values have TXCON bits 3-4 and TXCOM bit 3 set as well, and with
|
||||
* them I2S0 never takes the codec's BCLK/LRCK. */
|
||||
#ifdef IPOD_NANO3G
|
||||
#define I2STXCON_SETUP 0x0b100001
|
||||
#define I2STXCOM_START 0x6
|
||||
#else
|
||||
#define I2STXCON_SETUP 0xb100019
|
||||
#define I2STXCOM_START 0xe
|
||||
#endif
|
||||
|
||||
static volatile int locked = 0;
|
||||
static unsigned char dblbuf[2][WATERMARK_BYTES] CACHEALIGN_ATTR;
|
||||
static int active_dblbuf;
|
||||
|
|
@ -152,7 +164,7 @@ static void sink_dma_start(const void* addr, size_t size)
|
|||
sink_dma_stop();
|
||||
|
||||
pcm_remaining = size;
|
||||
I2STXCOM = 0xe;
|
||||
I2STXCOM = I2STXCOM_START;
|
||||
dma_play_callback((void*)addr);
|
||||
}
|
||||
|
||||
|
|
@ -162,9 +174,16 @@ static void sink_dma_start(const void* addr, size_t size)
|
|||
/* set the configured PCM frequency */
|
||||
static void sink_set_freq(uint16_t freq)
|
||||
{
|
||||
#ifdef HAVE_CS42L55
|
||||
static uint16_t last_clkcon3l = 0;
|
||||
uint16_t clkcon3l;
|
||||
#else
|
||||
/* Not a valid setting, so the first call always programs MCLK. The
|
||||
* CS42L55 driver starts MCLK itself; the WM8975 driver does not. */
|
||||
static uint16_t last_clkcon3l = 0xffff;
|
||||
#endif
|
||||
uint16_t clkcon3l = 0; /* OSC0 -> 12 MHz */
|
||||
|
||||
#ifdef HAVE_CS42L55
|
||||
/* For unknown reasons, s5l8702 I2S controller does not synchronize
|
||||
* with CS42L55 at 32000 Hz. To fix it, the CODEC is configured with
|
||||
* a sample rate of 48000 Hz and MCLK is decreased 1/3 to 8 Mhz,
|
||||
|
|
@ -175,9 +194,7 @@ static void sink_set_freq(uint16_t freq)
|
|||
freq = HW_FREQ_48;
|
||||
clkcon3l = 0x3028; /* PLL2 / 3 / 9 -> 8 MHz */
|
||||
}
|
||||
else {
|
||||
clkcon3l = 0; /* OSC0 -> 12 MHz */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* configure MCLK */
|
||||
/* TODO: maybe all CLKCON management should be moved to
|
||||
|
|
@ -201,7 +218,7 @@ static void sink_dma_init(void)
|
|||
|
||||
dmac_ch_init(&dma_play_ch, &dma_play_ch_cfg);
|
||||
|
||||
I2STXCON = 0xb100019;
|
||||
I2STXCON = I2STXCON_SETUP;
|
||||
I2SCLKCON = 1;
|
||||
|
||||
audiohw_preinit();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue