cd64aa2b10
The X3's line out is a bit hot, at ~4.3Vpp, so allow it to be backed off. (On my X3, backing it off to -6dB brings Vpp down to ~3.4V) Change-Id: Iea38ef1c6a1b183d0f8fb4eaf2bf9ed6b350a532
352 lines
9.4 KiB
C
352 lines
9.4 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
*
|
|
* Copyright (C) 2016 by Roman Stolyarov
|
|
*
|
|
* 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 "audio.h"
|
|
#include "sound.h"
|
|
#include "cpu.h"
|
|
#include "system.h"
|
|
#include "pcm_sw_volume.h"
|
|
#include "cs4398.h"
|
|
#include "kernel.h"
|
|
#include "button.h"
|
|
#include "settings.h"
|
|
|
|
#define PIN_CS_RST (32*1+10)
|
|
#define PIN_CODEC_PWRON (32*1+13)
|
|
#define PIN_AP_MUTE (32*1+14)
|
|
#define PIN_JD_CON (32*1+16)
|
|
|
|
static void pop_ctrl(const int val)
|
|
{
|
|
if(val)
|
|
__gpio_clear_pin(PIN_JD_CON);
|
|
else
|
|
__gpio_set_pin(PIN_JD_CON);
|
|
}
|
|
|
|
static void amp_enable(const int val)
|
|
{
|
|
if(val)
|
|
__gpio_set_pin(PIN_CODEC_PWRON);
|
|
else
|
|
__gpio_clear_pin(PIN_CODEC_PWRON);
|
|
}
|
|
|
|
static void dac_enable(const int val)
|
|
{
|
|
if(val)
|
|
__gpio_set_pin(PIN_CS_RST);
|
|
else
|
|
__gpio_clear_pin(PIN_CS_RST);
|
|
}
|
|
|
|
static void ap_mute(bool mute)
|
|
{
|
|
if(mute)
|
|
__gpio_clear_pin(PIN_AP_MUTE);
|
|
else
|
|
__gpio_set_pin(PIN_AP_MUTE);
|
|
}
|
|
|
|
static void audiohw_mute(bool mute)
|
|
{
|
|
if(mute)
|
|
cs4398_write_reg(CS4398_REG_MUTE, cs4398_read_reg(CS4398_REG_MUTE) | CS4398_MUTE_A | CS4398_MUTE_B);
|
|
else
|
|
cs4398_write_reg(CS4398_REG_MUTE, cs4398_read_reg(CS4398_REG_MUTE) & ~(CS4398_MUTE_A | CS4398_MUTE_B));
|
|
}
|
|
|
|
/* TODO: Note this is X3-specific! */
|
|
void audiohw_preinit(void)
|
|
{
|
|
cs4398_write_reg(CS4398_REG_MISC, CS4398_CPEN | CS4398_PDN);
|
|
cs4398_write_reg(CS4398_REG_MODECTL, CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST);
|
|
cs4398_write_reg(CS4398_REG_VOLMIX, CS4398_ATAPI_A_L | CS4398_ATAPI_B_R);
|
|
cs4398_write_reg(CS4398_REG_MUTE, CS4398_MUTEP_LOW);
|
|
cs4398_write_reg(CS4398_REG_VOL_A, 0xff);
|
|
cs4398_write_reg(CS4398_REG_VOL_B, 0xff);
|
|
cs4398_write_reg(CS4398_REG_RAMPFILT, CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
|
|
cs4398_write_reg(CS4398_REG_MISC, CS4398_CPEN);
|
|
}
|
|
|
|
void audiohw_init(void)
|
|
{
|
|
__gpio_as_func1(3*32+12); // BCK - BCLK pin AA20 func 1
|
|
__gpio_as_func0(3*32+13); // LRCK - SYNC pin W19 func 0
|
|
__gpio_as_func2(4*32+5); // MCLK - SCLK_RSTN - E20 fund 2
|
|
__gpio_as_func0(4*32+7); // DO - SDATO pin Y19 func 0
|
|
|
|
pop_ctrl(0);
|
|
ap_mute(true);
|
|
amp_enable(0);
|
|
dac_enable(0);
|
|
|
|
__gpio_as_output(PIN_JD_CON);
|
|
__gpio_as_output(PIN_AP_MUTE);
|
|
__gpio_as_output(PIN_CODEC_PWRON);
|
|
__gpio_as_output(PIN_CS_RST);
|
|
|
|
mdelay(100);
|
|
amp_enable(1);
|
|
|
|
/* set AIC clk PLL1 */
|
|
__cpm_select_i2sclk_pll();
|
|
__cpm_select_i2sclk_pll1();
|
|
|
|
__cpm_enable_pll_change();
|
|
__cpm_set_i2sdiv(43-1);
|
|
|
|
__cpm_start_aic();
|
|
|
|
/* Init AIC */
|
|
__aic_play_lastsample(); /* on FIFO underflow. Versus 0.. */
|
|
__i2s_enable_sclk();
|
|
__i2s_external_codec();
|
|
__i2s_select_msbjustified();
|
|
__i2s_as_master();
|
|
__i2s_enable_transmit_dma();
|
|
__i2s_set_transmit_trigger(24);
|
|
__i2s_set_oss_sample_size(16);
|
|
__i2s_enable();
|
|
|
|
/* Init DAC */
|
|
dac_enable(1);
|
|
udelay(1);
|
|
audiohw_preinit();
|
|
}
|
|
|
|
static int vol_tenthdb2hw(const int tdb)
|
|
{
|
|
if (tdb < CS4398_VOLUME_MIN) {
|
|
return 0xff;
|
|
} else if (tdb > CS4398_VOLUME_MAX) {
|
|
return 0x00;
|
|
} else {
|
|
return (-tdb/5);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_LINEOUT_DETECTION
|
|
static int real_vol_l, real_vol_r;
|
|
#endif
|
|
|
|
static void jz4760_set_vol(int vol_l, int vol_r)
|
|
{
|
|
uint8_t val = cs4398_read_reg(CS4398_REG_MISC) &~ CS4398_FREEZE;
|
|
cs4398_write_reg(CS4398_REG_MISC, val | CS4398_FREEZE);
|
|
cs4398_write_reg(CS4398_REG_VOL_A, vol_tenthdb2hw(vol_l));
|
|
cs4398_write_reg(CS4398_REG_VOL_B, vol_tenthdb2hw(vol_r));
|
|
cs4398_write_reg(CS4398_REG_MISC, val);
|
|
}
|
|
|
|
/* The xDuoo X3's line out is a bit on the hot side, with about 4.3Vpp
|
|
so allow the level to be backed off by using the global volume_limit */
|
|
void audiohw_set_volume(int vol_l, int vol_r)
|
|
{
|
|
#ifdef HAVE_LINEOUT_DETECTION
|
|
real_vol_l = vol_l;
|
|
real_vol_r = vol_r;
|
|
|
|
if (lineout_inserted()) {
|
|
vol_l = vol_r = global_settings.volume_limit;
|
|
}
|
|
#endif
|
|
jz4760_set_vol(vol_l, vol_r);
|
|
}
|
|
|
|
void audiohw_set_lineout_volume(int vol_l, int vol_r)
|
|
{
|
|
(void)vol_l;
|
|
(void)vol_r;
|
|
|
|
#ifdef HAVE_LINEOUT_DETECTION
|
|
if (lineout_inserted()) {
|
|
jz4760_set_vol(global_settings.volume_limit, global_settings.volume_limit);
|
|
} else {
|
|
jz4760_set_vol(real_vol_l, real_vol_r);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void audiohw_set_filter_roll_off(int value)
|
|
{
|
|
if (value == 0) {
|
|
cs4398_write_reg(CS4398_REG_RAMPFILT, cs4398_read_reg(CS4398_REG_RAMPFILT) & ~CS4398_FILT_SEL);
|
|
} else {
|
|
cs4398_write_reg(CS4398_REG_RAMPFILT, cs4398_read_reg(CS4398_REG_RAMPFILT) | CS4398_FILT_SEL);
|
|
}
|
|
}
|
|
|
|
void pll1_init(unsigned int freq);
|
|
void pll1_disable(void);
|
|
|
|
#if CFG_EXTAL != 12000000
|
|
#error "non-12MHz XTAL needs new audio rates calculated!"
|
|
#endif
|
|
|
|
void audiohw_set_frequency(int fsel)
|
|
{
|
|
unsigned int pll1_speed;
|
|
unsigned short mclk_div, bclk_div, func_mode;
|
|
|
|
// bclk is 2,3,4,6,8,12 ONLY
|
|
// mclk is 1..512
|
|
|
|
switch(fsel)
|
|
{
|
|
case HW_FREQ_8: // 0.512 MHz
|
|
pll1_speed = 426000000/4;
|
|
mclk_div = 208/4;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_11: // 0.7056 MHz
|
|
pll1_speed = 508000000 / 3;
|
|
mclk_div = 180 / 3;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_12: // 0.768 MHz
|
|
pll1_speed = 516000000/2/3;
|
|
mclk_div = 168/2/3;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_16: // 1.024 MHz
|
|
pll1_speed = 426000000/4;
|
|
mclk_div = 104/4;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_22: // 1.4112 MHz
|
|
pll1_speed = 508000000 / 3;
|
|
mclk_div = 90 / 3;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_24: // 1.536 MHz
|
|
pll1_speed = 516000000/2/3;
|
|
mclk_div = 84/2/3;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_32: // 2.048 MHz
|
|
pll1_speed = 426000000/4;
|
|
mclk_div = 52/4;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_44: // 2.8224 MHz
|
|
pll1_speed = 508000000 / 3;
|
|
mclk_div = 45 / 3;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_48: // 3.072 MHz
|
|
pll1_speed = 516000000/2/3;
|
|
mclk_div = 42/2/3;
|
|
bclk_div = 4;
|
|
func_mode = 0;
|
|
break;
|
|
case HW_FREQ_64: // 4.096 MHz
|
|
pll1_speed = 426000000/4;
|
|
mclk_div = 52/4;
|
|
bclk_div = 2;
|
|
func_mode = 1;
|
|
break;
|
|
case HW_FREQ_88: // 5.6448 MHz
|
|
pll1_speed = 508000000 / 3;
|
|
mclk_div = 45 / 3;
|
|
bclk_div = 2;
|
|
func_mode = 1;
|
|
break;
|
|
case HW_FREQ_96: // 6.144 MHz
|
|
pll1_speed = 516000000/2/3;
|
|
mclk_div = 42/2/3;
|
|
bclk_div = 2;
|
|
func_mode = 1;
|
|
break;
|
|
case HW_FREQ_176: // 11.2896 MHz
|
|
pll1_speed = 508000000*2;
|
|
mclk_div = 45;
|
|
bclk_div = 2;
|
|
func_mode = 2;
|
|
break;
|
|
case HW_FREQ_192: // 12.288 MHz
|
|
pll1_speed = 516000000;
|
|
mclk_div = 42/2;
|
|
bclk_div = 2;
|
|
func_mode = 2;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
ap_mute(true);
|
|
__i2s_stop_bitclk();
|
|
|
|
/* 0 = Single-Speed Mode (<50KHz);
|
|
1 = Double-Speed Mode (50-100KHz);
|
|
2 = Quad-Speed Mode; (100-200KHz) */
|
|
cs4398_write_reg(CS4398_REG_MODECTL, (cs4398_read_reg(CS4398_REG_MODECTL) & ~(CS4398_FM_MASK|CS4398_DEM_MASK)) | func_mode | CS4398_DEM_NONE);
|
|
if (func_mode == 2)
|
|
cs4398_write_reg(CS4398_REG_MISC, cs4398_read_reg(CS4398_REG_MISC) | CS4398_MCLKDIV2);
|
|
else
|
|
cs4398_write_reg(CS4398_REG_MISC, cs4398_read_reg(CS4398_REG_MISC) & ~CS4398_MCLKDIV2);
|
|
|
|
if (pll1_speed == 0) {
|
|
pll1_disable();
|
|
__cpm_select_i2sclk_exclk();
|
|
} else {
|
|
__cpm_select_i2sclk_pll();
|
|
__cpm_select_i2sclk_pll1();
|
|
pll1_init(pll1_speed);
|
|
}
|
|
__cpm_enable_pll_change();
|
|
__cpm_set_i2sdiv(mclk_div-1);
|
|
__i2s_set_i2sdiv(bclk_div-1);
|
|
__i2s_start_bitclk();
|
|
mdelay(20);
|
|
ap_mute(false);
|
|
}
|
|
|
|
void audiohw_postinit(void)
|
|
{
|
|
sleep(HZ);
|
|
audiohw_mute(false);
|
|
ap_mute(false);
|
|
pop_ctrl(1);
|
|
}
|
|
|
|
void audiohw_close(void)
|
|
{
|
|
pop_ctrl(0);
|
|
sleep(HZ/10);
|
|
ap_mute(true);
|
|
audiohw_mute(true);
|
|
amp_enable(0);
|
|
dac_enable(0);
|
|
__i2s_disable();
|
|
__cpm_stop_aic();
|
|
pll1_disable();
|
|
sleep(HZ);
|
|
pop_ctrl(1);
|
|
}
|