rockbox/firmware/target/mips/ingenic_x1000/aic-x1000.c

120 lines
3.6 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* 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 "system.h"
#include "aic-x1000.h"
#include "gpio-x1000.h"
#include "x1000/aic.h"
#include "x1000/cpm.h"
/* Given a rational number m/n < 1, find its representation as a continued
* fraction [0; a1, a2, a3, ..., a_k]. At most "cnt" terms are calculated
* and written out to "buf". Returns the number of terms written; the result
* is complete if this value is less than "cnt", and may be incomplete if it
* is equal to "cnt". (Note the leading zero term is not written to "buf".)
*/
static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt)
{
unsigned wrote = 0;
unsigned a = m / n;
while(cnt--) {
unsigned tmp = n;
n = m - n * a;
if(n == 0)
break;
m = tmp;
a = m / n;
*buf++ = a;
wrote++;
}
return wrote;
}
/* Given a finite continued fraction [0; buf[0], buf[1], ..., buf[count-1]],
* calculate the rational number m/n which it represents. Returns m and n.
* If count is zero, then m and n are undefined.
*/
static void cf_expand(const unsigned* buf, unsigned count,
unsigned* m, unsigned* n)
{
if(count == 0)
return;
unsigned i = count - 1;
unsigned mx = 1, nx = buf[i];
while(i--) {
unsigned tmp = nx;
nx = mx + buf[i] * nx;
mx = tmp;
}
*m = mx;
*n = nx;
}
int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult)
{
/* get the input clock rate */
uint32_t src_freq = clk_get(clksrc);
/* reject invalid parameters */
if(mult % 64 != 0)
return -1;
if(clksrc == X1000_EXCLK_FREQ) {
if(mult != 0)
return -1;
jz_writef(AIC_I2SCR, STPBK(1));
jz_writef(CPM_I2SCDR, CS(0), CE(0));
REG_AIC_I2SDIV = X1000_EXCLK_FREQ / 64 / fs;
} else {
if(mult == 0)
return -1;
if(fs*mult > src_freq)
return -1;
/* calculate best rational approximation that fits our constraints */
unsigned m = 0, n = 0;
unsigned buf[16];
unsigned cnt = cf_derive(fs*mult, src_freq, &buf[0], 16);
do {
cf_expand(&buf[0], cnt, &m, &n);
cnt -= 1;
} while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m));
/* wrong values */
if(cnt == 0 || n == 0 || m == 0)
return -1;
jz_writef(AIC_I2SCR, STPBK(1));
jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0),
CS(1), CE(1), DIV_M(m), DIV_N(n));
jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1);
REG_AIC_I2SDIV = (mult / 64) - 1;
}
jz_writef(AIC_I2SCR, STPBK(0));
return 0;
}