rockbox/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
Aidan MacDonald 4c60bc9e68 New port: Shanling Q1 native
- Audio playback works
- Touchscreen and buttons work
- Bootloader works and is capable of dual boot
- Plugins are working
- Cabbiev2 theme has been ported
- Stable for general usage

Thanks to Marc Aarts for porting Cabbiev2 and plugin bitmaps.

There's a few minor known issues:

- Bootloader must be installed manually using 'usbboot' as there is
  no support in jztool yet.

- Keymaps may be lacking, need further testing and feedback.

- Some plugins may not be fully adapted to the screen size and could
  benefit from further tweaking.

- LCD shows abnormal effects under some circumstances: for example,
  after viewing a mostly black screen an afterimage appears briefly
  when going back to a brightly-lit screen. Sudden power-off without
  proper shutdown of the backlight causes a "dissolving" effect.

- CW2015 battery reporting driver is buggy, and disabled for now.
  Battery reporting is currently voltage-based using the AXP192.

Change-Id: I635e83f02a880192c5a82cb0861ad3a61c137c3a
2021-07-13 22:01:33 +01:00

191 lines
5 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* 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 "audiohw.h"
#include "system.h"
#include "pcm_sampr.h"
#include "aic-x1000.h"
#include "i2c-x1000.h"
#include "gpio-x1000.h"
#include "x1000/aic.h"
#include "x1000/cpm.h"
/* Codec has an dedicated oscillator connected, so it can operate
* as i2s master or slave. I can't distinguish any difference in
* terms of audio quality or power consumption. Code is left here
* for reference in case it proves useful to change it. */
#define CODEC_MASTER_MODE 0
static int cur_fsel = HW_FREQ_48;
static int cur_vol_l = 0, cur_vol_r = 0;
static int cur_filter = 0;
static enum es9218_amp_mode cur_amp_mode = ES9218_AMP_MODE_1VRMS;
static void codec_start(void)
{
es9218_open();
es9218_mute(true);
es9218_set_iface_role(CODEC_MASTER_MODE ? ES9218_IFACE_ROLE_MASTER
: ES9218_IFACE_ROLE_SLAVE);
es9218_set_iface_format(ES9218_IFACE_FORMAT_I2S, ES9218_IFACE_BITS_32);
es9218_set_dpll_bandwidth(10);
es9218_set_thd_compensation(true);
es9218_set_thd_coeffs(0, 0);
audiohw_set_filter_roll_off(cur_filter);
audiohw_set_frequency(cur_fsel);
audiohw_set_volume(cur_vol_l, cur_vol_r);
es9218_set_amp_mode(cur_amp_mode);
}
static void codec_stop(void)
{
es9218_mute(true);
es9218_close();
mdelay(1);
}
void audiohw_init(void)
{
/* Configure AIC */
aic_set_external_codec(true);
aic_set_i2s_mode(CODEC_MASTER_MODE ? AIC_I2S_SLAVE_MODE
: AIC_I2S_MASTER_MODE);
aic_enable_i2s_bit_clock(true);
/* Open DAC driver */
i2c_x1000_set_freq(1, I2C_FREQ_400K);
codec_start();
}
void audiohw_postinit(void)
{
es9218_mute(false);
}
void audiohw_close(void)
{
codec_stop();
}
void audiohw_set_frequency(int fsel)
{
int sampr = hw_freq_sampr[fsel];
/* choose clock gear setting, in line with the OF */
enum es9218_clock_gear clkgear;
if(sampr <= 48000)
clkgear = ES9218_CLK_GEAR_4;
else if(sampr <= 96000)
clkgear = ES9218_CLK_GEAR_2;
else
clkgear = ES9218_CLK_GEAR_1;
aic_enable_i2s_bit_clock(false);
es9218_set_clock_gear(clkgear);
if(CODEC_MASTER_MODE)
es9218_set_nco_frequency(sampr);
else
aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, 64);
aic_enable_i2s_bit_clock(true);
/* save frequency selection */
cur_fsel = fsel;
}
static int round_step_up(int x, int step)
{
int rem = x % step;
if(rem > 0)
rem -= step;
return x - rem;
}
void audiohw_set_volume(int vol_l, int vol_r)
{
/* save volume */
cur_vol_l = vol_l;
cur_vol_r = vol_r;
/* adjust the amp setting first */
int amp = round_step_up(MAX(vol_l, vol_r), ES9218_AMP_VOLUME_STEP);
amp = MIN(amp, ES9218_AMP_VOLUME_MAX);
amp = MAX(amp, ES9218_AMP_VOLUME_MIN);
/* adjust digital volumes */
vol_l -= amp;
vol_l = MIN(vol_l, ES9218_DIG_VOLUME_MAX);
vol_l = MAX(vol_l, ES9218_DIG_VOLUME_MIN);
vol_r -= amp;
vol_r = MIN(vol_r, ES9218_DIG_VOLUME_MAX);
vol_r = MAX(vol_r, ES9218_DIG_VOLUME_MIN);
/* program DAC */
es9218_set_amp_volume(amp);
es9218_set_dig_volume(vol_l, vol_r);
}
void audiohw_set_filter_roll_off(int value)
{
cur_filter = value;
es9218_set_filter(value);
}
void audiohw_set_power_mode(int mode)
{
enum es9218_amp_mode new_amp_mode;
if(mode == 0)
new_amp_mode = ES9218_AMP_MODE_2VRMS;
else
new_amp_mode = ES9218_AMP_MODE_1VRMS;
if(new_amp_mode != cur_amp_mode) {
codec_stop();
cur_amp_mode = new_amp_mode;
codec_start();
es9218_mute(false);
}
}
void es9218_set_power_pin(int level)
{
gpio_set_level(GPIO_ES9218_POWER, level ? 1 : 0);
}
void es9218_set_reset_pin(int level)
{
gpio_set_level(GPIO_ES9218_RESET, level ? 1 : 0);
}
uint32_t es9218_get_mclk(void)
{
/* Measured by running the DAC in asynchronous I2S slave mode,
* and reading back the DPLL number from regs 0x42-0x45 while
* playing back 44.1 KHz audio.
*
* CLK = (44_100 * 2**32) / 0x4b46e5
* = 38_393_403.29532737
* ~ 38.4 Mhz
*/
return 38400000;
}