diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-30 19:56:44 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-30 19:17:50 +0000 |
commit | f63edb52ef8ecf18520926b40b3c61db37081a9d (patch) | |
tree | 29c36d3f247d7bab2f547d76655ac81fa8a71946 /firmware/target/mips/ingenic_x1000/aic-x1000.c | |
parent | c78ba1aa689b178ebb73b2730bc1b13697371fbf (diff) | |
download | rockbox-f63edb52ef8ecf18520926b40b3c61db37081a9d.tar.gz rockbox-f63edb52ef8ecf18520926b40b3c61db37081a9d.zip |
x1000: refactor AIC initialization
Have pcm-x1000 handle most work, so target's audiohw code touches
only the relevant settings.
Change-Id: Icf3d1b7ca428ac50a5a16ecec39ed8186ac5ae13
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/aic-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/aic-x1000.c | 132 |
1 files changed, 93 insertions, 39 deletions
diff --git a/firmware/target/mips/ingenic_x1000/aic-x1000.c b/firmware/target/mips/ingenic_x1000/aic-x1000.c index a0e509d3b6..1d1768d4f9 100644 --- a/firmware/target/mips/ingenic_x1000/aic-x1000.c +++ b/firmware/target/mips/ingenic_x1000/aic-x1000.c | |||
@@ -31,12 +31,12 @@ | |||
31 | * is complete if this value is less than "cnt", and may be incomplete if it | 31 | * is complete if this value is less than "cnt", and may be incomplete if it |
32 | * is equal to "cnt". (Note the leading zero term is not written to "buf".) | 32 | * is equal to "cnt". (Note the leading zero term is not written to "buf".) |
33 | */ | 33 | */ |
34 | static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt) | 34 | static uint32_t cf_derive(uint32_t m, uint32_t n, uint32_t* buf, uint32_t cnt) |
35 | { | 35 | { |
36 | unsigned wrote = 0; | 36 | uint32_t wrote = 0; |
37 | unsigned a = m / n; | 37 | uint32_t a = m / n; |
38 | while(cnt--) { | 38 | while(cnt--) { |
39 | unsigned tmp = n; | 39 | uint32_t tmp = n; |
40 | n = m - n * a; | 40 | n = m - n * a; |
41 | if(n == 0) | 41 | if(n == 0) |
42 | break; | 42 | break; |
@@ -54,16 +54,16 @@ static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt) | |||
54 | * calculate the rational number m/n which it represents. Returns m and n. | 54 | * calculate the rational number m/n which it represents. Returns m and n. |
55 | * If count is zero, then m and n are undefined. | 55 | * If count is zero, then m and n are undefined. |
56 | */ | 56 | */ |
57 | static void cf_expand(const unsigned* buf, unsigned count, | 57 | static void cf_expand(const uint32_t* buf, uint32_t count, |
58 | unsigned* m, unsigned* n) | 58 | uint32_t* m, uint32_t* n) |
59 | { | 59 | { |
60 | if(count == 0) | 60 | if(count == 0) |
61 | return; | 61 | return; |
62 | 62 | ||
63 | unsigned i = count - 1; | 63 | uint32_t i = count - 1; |
64 | unsigned mx = 1, nx = buf[i]; | 64 | uint32_t mx = 1, nx = buf[i]; |
65 | while(i--) { | 65 | while(i--) { |
66 | unsigned tmp = nx; | 66 | uint32_t tmp = nx; |
67 | nx = mx + buf[i] * nx; | 67 | nx = mx + buf[i] * nx; |
68 | mx = tmp; | 68 | mx = tmp; |
69 | } | 69 | } |
@@ -72,48 +72,102 @@ static void cf_expand(const unsigned* buf, unsigned count, | |||
72 | *n = nx; | 72 | *n = nx; |
73 | } | 73 | } |
74 | 74 | ||
75 | int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult) | 75 | static int calc_i2s_clock_params(x1000_clk_t clksrc, |
76 | uint32_t fs, uint32_t mult, | ||
77 | uint32_t* div_m, uint32_t* div_n, | ||
78 | uint32_t* i2sdiv) | ||
76 | { | 79 | { |
77 | /* get the input clock rate */ | 80 | if(clksrc == X1000_CLK_EXCLK) { |
78 | uint32_t src_freq = clk_get(clksrc); | 81 | /* EXCLK mode bypasses the CPM clock so it's more limited */ |
82 | *div_m = 0; | ||
83 | *div_n = 0; | ||
84 | *i2sdiv = X1000_EXCLK_FREQ / 64 / fs; | ||
85 | |||
86 | /* clamp to maximum value */ | ||
87 | if(*i2sdiv > 0x200) | ||
88 | *i2sdiv = 0x200; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
79 | 92 | ||
80 | /* reject invalid parameters */ | 93 | /* ensure a valid clock was selected */ |
94 | if(clksrc != X1000_CLK_SCLK_A && | ||
95 | clksrc != X1000_CLK_MPLL) | ||
96 | return -1; | ||
97 | |||
98 | /* ensure bit clock constraint is respected */ | ||
81 | if(mult % 64 != 0) | 99 | if(mult % 64 != 0) |
82 | return -1; | 100 | return -1; |
83 | 101 | ||
84 | if(clksrc == X1000_EXCLK_FREQ) { | 102 | /* ensure master clock frequency is not too high */ |
85 | if(mult != 0) | 103 | if(fs > UINT32_MAX/mult) |
86 | return -1; | 104 | return -1; |
105 | |||
106 | /* get frequencies */ | ||
107 | uint32_t tgt_freq = fs * mult; | ||
108 | uint32_t src_freq = clk_get(clksrc); | ||
109 | |||
110 | /* calculate best rational approximation fitting hardware constraints */ | ||
111 | uint32_t m = 0, n = 0; | ||
112 | uint32_t buf[16]; | ||
113 | uint32_t cnt = cf_derive(tgt_freq, src_freq, &buf[0], 16); | ||
114 | do { | ||
115 | cf_expand(&buf[0], cnt, &m, &n); | ||
116 | cnt -= 1; | ||
117 | } while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m)); | ||
87 | 118 | ||
88 | jz_writef(AIC_I2SCR, STPBK(1)); | 119 | /* unrepresentable */ |
120 | if(cnt == 0 || n == 0 || m == 0) | ||
121 | return -1; | ||
122 | |||
123 | *div_m = m; | ||
124 | *div_n = n; | ||
125 | *i2sdiv = mult / 64; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | uint32_t aic_calc_i2s_clock(x1000_clk_t clksrc, uint32_t fs, uint32_t mult) | ||
130 | { | ||
131 | uint32_t m, n, i2sdiv; | ||
132 | if(calc_i2s_clock_params(clksrc, fs, mult, &m, &n, &i2sdiv)) | ||
133 | return 0; | ||
134 | |||
135 | unsigned long long rate = clk_get(clksrc); | ||
136 | rate *= m; | ||
137 | rate /= n * i2sdiv; /* this multiply can't overflow. */ | ||
138 | |||
139 | /* clamp */ | ||
140 | if(rate > 0xffffffffull) | ||
141 | rate = 0xffffffff; | ||
142 | |||
143 | return rate; | ||
144 | } | ||
145 | |||
146 | int aic_set_i2s_clock(x1000_clk_t clksrc, uint32_t fs, uint32_t mult) | ||
147 | { | ||
148 | uint32_t m, n, i2sdiv; | ||
149 | if(calc_i2s_clock_params(clksrc, fs, mult, &m, &n, &i2sdiv)) | ||
150 | return -1; | ||
151 | |||
152 | /* turn off bit clock */ | ||
153 | bool bitclock_en = !jz_readf(AIC_I2SCR, STPBK); | ||
154 | jz_writef(AIC_I2SCR, STPBK(1)); | ||
155 | |||
156 | /* handle master clock */ | ||
157 | if(clksrc == X1000_CLK_EXCLK) { | ||
89 | jz_writef(CPM_I2SCDR, CS(0), CE(0)); | 158 | jz_writef(CPM_I2SCDR, CS(0), CE(0)); |
90 | REG_AIC_I2SDIV = X1000_EXCLK_FREQ / 64 / fs; | ||
91 | } else { | 159 | } else { |
92 | if(mult == 0) | ||
93 | return -1; | ||
94 | if(fs*mult > src_freq) | ||
95 | return -1; | ||
96 | |||
97 | /* calculate best rational approximation that fits our constraints */ | ||
98 | unsigned m = 0, n = 0; | ||
99 | unsigned buf[16]; | ||
100 | unsigned cnt = cf_derive(fs*mult, src_freq, &buf[0], 16); | ||
101 | do { | ||
102 | cf_expand(&buf[0], cnt, &m, &n); | ||
103 | cnt -= 1; | ||
104 | } while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m)); | ||
105 | |||
106 | /* wrong values */ | ||
107 | if(cnt == 0 || n == 0 || m == 0) | ||
108 | return -1; | ||
109 | |||
110 | jz_writef(AIC_I2SCR, STPBK(1)); | ||
111 | jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0), | 160 | jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0), |
112 | CS(1), CE(1), DIV_M(m), DIV_N(n)); | 161 | CS(1), CE(1), DIV_M(m), DIV_N(n)); |
113 | jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1); | 162 | jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1); |
114 | REG_AIC_I2SDIV = (mult / 64) - 1; | ||
115 | } | 163 | } |
116 | 164 | ||
117 | jz_writef(AIC_I2SCR, STPBK(0)); | 165 | /* set bit clock divider */ |
166 | REG_AIC_I2SDIV = i2sdiv - 1; | ||
167 | |||
168 | /* re-enable the bit clock */ | ||
169 | if(bitclock_en) | ||
170 | jz_writef(AIC_I2SCR, STPBK(0)); | ||
171 | |||
118 | return 0; | 172 | return 0; |
119 | } | 173 | } |