summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/audio')
-rw-r--r--firmware/drivers/audio/ak4376.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/firmware/drivers/audio/ak4376.c b/firmware/drivers/audio/ak4376.c
new file mode 100644
index 0000000000..494bbabfa4
--- /dev/null
+++ b/firmware/drivers/audio/ak4376.c
@@ -0,0 +1,274 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "audiohw.h"
23#include "sound.h"
24#include "panic.h"
25#include "pcm_sampr.h"
26#include "pcm_sw_volume.h"
27#include "system.h"
28#include "i2c-async.h"
29
30#ifndef HAVE_SW_VOLUME_CONTROL
31# error "AK4376 requires HAVE_SW_VOLUME_CONTROL!"
32#endif
33
34/* NOTE: At present, only the FiiO M3K uses this driver so the handling of
35 * the clock / audio interface is limited to I2S slave, 16-bit samples, with
36 * DAC master clock provided directly on the MCLK input pin, fitting the
37 * clock setup of the M3K.
38 *
39 * Feel free to expand upon this if another target ever needs this driver.
40 */
41
42/* Converts HW_FREQ_XX constants to register values */
43static const int ak4376_fsel_to_hw[] = {
44 HW_HAVE_192_(AK4376_FS_192,)
45 HW_HAVE_176_(AK4376_FS_176,)
46 HW_HAVE_96_(AK4376_FS_96,)
47 HW_HAVE_88_(AK4376_FS_88,)
48 HW_HAVE_64_(AK4376_FS_64,)
49 HW_HAVE_48_(AK4376_FS_48,)
50 HW_HAVE_44_(AK4376_FS_44,)
51 HW_HAVE_32_(AK4376_FS_32,)
52 HW_HAVE_24_(AK4376_FS_24,)
53 HW_HAVE_22_(AK4376_FS_22,)
54 HW_HAVE_16_(AK4376_FS_16,)
55 HW_HAVE_12_(AK4376_FS_12,)
56 HW_HAVE_11_(AK4376_FS_11,)
57 HW_HAVE_8_(AK4376_FS_8,)
58};
59
60static struct ak4376 {
61 int fsel;
62 int low_mode;
63 int regs[AK4376_NUM_REGS];
64} ak4376;
65
66void ak4376_init(void)
67{
68 /* Initialize DAC state */
69 ak4376.fsel = HW_FREQ_48;
70 ak4376.low_mode = 0;
71 for(int i = 0; i < AK4376_NUM_REGS; ++i)
72 ak4376.regs[i] = -1;
73
74 /* Initial reset after power-on */
75 ak4376_set_pdn_pin(0);
76 mdelay(1);
77 ak4376_set_pdn_pin(1);
78 mdelay(1);
79
80 static const int init_config[] = {
81 /* Ensure HPRHZ, HPLHZ are 0 */
82 AK4376_REG_OUTPUT_MODE, 0x00,
83 /* Mute all volume controls */
84 AK4376_REG_MIXER, 0x00,
85 AK4376_REG_LCH_VOLUME, 0x80,
86 AK4376_REG_RCH_VOLUME, 0x00,
87 AK4376_REG_AMP_VOLUME, 0x00,
88 /* Clock source = MCLK, divider = 1 */
89 AK4376_REG_DAC_CLK_SRC, 0x00,
90 AK4376_REG_DAC_CLK_DIV, 0x00,
91 /* I2S slave mode, 16-bit samples */
92 AK4376_REG_AUDIO_IF_FMT, 0x03,
93 /* Recommended by datasheet */
94 AK4376_REG_ADJUST1, 0x20,
95 AK4376_REG_ADJUST2, 0x05,
96 /* Power controls */
97 AK4376_REG_PWR2, 0x33,
98 AK4376_REG_PWR3, 0x01,
99 AK4376_REG_PWR4, 0x03,
100 };
101
102 /* Write initial configuration prior to power-up */
103 for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
104 ak4376_write(init_config[i], init_config[i+1]);
105
106 /* Initial frequency setting, also handles DAC/amp power-up */
107 audiohw_set_frequency(HW_FREQ_48);
108}
109
110void ak4376_close(void)
111{
112 /* Shut off power */
113 ak4376_write(AK4376_REG_PWR3, 0x00);
114 ak4376_write(AK4376_REG_PWR4, 0x00);
115 ak4376_write(AK4376_REG_PWR2, 0x00);
116
117 /* PDN pin low */
118 ak4376_set_pdn_pin(0);
119}
120
121void ak4376_write(int reg, int value)
122{
123 /* Ensure value is sensible and differs from the last set value */
124 if((value & 0xff) == value && ak4376.regs[reg] != value) {
125 int r = i2c_reg_write1(AK4376_BUS, AK4376_ADDR, reg, value);
126 if(r == I2C_STATUS_OK)
127 ak4376.regs[reg] = value;
128 else
129 ak4376.regs[reg] = -1;
130 }
131}
132
133int ak4376_read(int reg)
134{
135 /* Only read from I2C if we don't already know the value */
136 if(ak4376.regs[reg] < 0)
137 ak4376.regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg);
138
139 return ak4376.regs[reg];
140}
141
142static int round_step_up(int x, int step)
143{
144 int rem = x % step;
145 if(rem > 0)
146 rem -= step;
147 return x - rem;
148}
149
150static void calc_volumes(int vol, int* mix, int* dig, int* sw)
151{
152 /* Mixer can divide by 2, which gives an extra -6 dB adjustment */
153 if(vol < AK4376_DIG_VOLUME_MIN) {
154 *mix |= AK4376_MIX_HALF;
155 vol += 60;
156 }
157
158 *dig = round_step_up(vol, AK4376_DIG_VOLUME_STEP);
159 *dig = MIN(*dig, AK4376_DIG_VOLUME_MAX);
160 *dig = MAX(*dig, AK4376_DIG_VOLUME_MIN);
161 vol -= *dig;
162
163 /* Seems that this is the allowable range for software volume */
164 *sw = MIN(vol, 60);
165 *sw = MAX(*sw, -730);
166 vol -= *sw;
167}
168
169static int dig_vol_to_hw(int vol)
170{
171 if(vol < AK4376_DIG_VOLUME_MIN) return 0;
172 if(vol > AK4376_DIG_VOLUME_MAX) return 31;
173 return (vol - AK4376_DIG_VOLUME_MIN) / AK4376_DIG_VOLUME_STEP + 1;
174}
175
176static int amp_vol_to_hw(int vol)
177{
178 if(vol < AK4376_AMP_VOLUME_MIN) return 0;
179 if(vol > AK4376_AMP_VOLUME_MAX) return 14;
180 return (vol - AK4376_AMP_VOLUME_MIN) / AK4376_AMP_VOLUME_STEP + 1;
181}
182
183void audiohw_set_volume(int vol_l, int vol_r)
184{
185 int amp;
186 int mix_l = AK4376_MIX_LCH, dig_l, sw_l;
187 int mix_r = AK4376_MIX_RCH, dig_r, sw_r;
188
189 if(vol_l <= AK4376_MIN_VOLUME && vol_r <= AK4376_MIN_VOLUME) {
190 /* Special case for full mute */
191 amp = AK4376_AMP_VOLUME_MUTE;
192 dig_l = dig_r = AK4376_DIG_VOLUME_MUTE;
193 sw_l = sw_r = PCM_MUTE_LEVEL;
194 } else {
195 /* Amp is a mono control -- calculate based on the loudest channel.
196 * The quieter channel then gets reduced more by digital controls. */
197 amp = round_step_up(MAX(vol_l, vol_r), AK4376_AMP_VOLUME_STEP);
198 amp = MIN(amp, AK4376_AMP_VOLUME_MAX);
199 amp = MAX(amp, AK4376_AMP_VOLUME_MIN);
200
201 /* Other controls are stereo */
202 calc_volumes(vol_l - amp, &mix_l, &dig_l, &sw_l);
203 calc_volumes(vol_r - amp, &mix_r, &dig_r, &sw_r);
204 }
205
206 ak4376_write(AK4376_REG_MIXER, (mix_l & 0xf) | ((mix_r & 0xf) << 4));
207 ak4376_write(AK4376_REG_LCH_VOLUME, dig_vol_to_hw(dig_l) | (1 << 7));
208 ak4376_write(AK4376_REG_RCH_VOLUME, dig_vol_to_hw(dig_r));
209 ak4376_write(AK4376_REG_AMP_VOLUME, amp_vol_to_hw(amp));
210 pcm_set_master_volume(sw_l, sw_r);
211}
212
213void audiohw_set_filter_roll_off(int val)
214{
215 int reg = ak4376_read(AK4376_REG_FILTER);
216 reg &= ~0xc0;
217 reg |= (val & 3) << 6;
218 ak4376_write(AK4376_REG_FILTER, reg);
219}
220
221void audiohw_set_frequency(int fsel)
222{
223 /* Determine master clock multiplier */
224 int mult = ak4376_set_mclk_freq(fsel, false);
225
226 /* Calculate clock mode for frequency. Multipliers of 32/64 are only
227 * for rates >= 256 KHz which are not supported by Rockbox, so they
228 * are commented out -- but they're in the correct place. */
229 int clock_mode = ak4376_fsel_to_hw[fsel];
230 switch(mult) {
231 /* case 32: */
232 case 256:
233 break;
234 /* case 64: */
235 case 512:
236 clock_mode |= 0x20;
237 break;
238 case 1024:
239 clock_mode |= 0x40;
240 break;
241 case 128:
242 clock_mode |= 0x60;
243 break;
244 default:
245 panicf("ak4376: bad master clock multiple %d", mult);
246 return;
247 }
248
249 /* Handle the DSMLP bit in the MODE_CTRL register */
250 int mode_ctrl = 0x00;
251 if(ak4376.low_mode || hw_freq_sampr[fsel] <= SAMPR_12)
252 mode_ctrl |= 0x40;
253
254 /* Program the new settings */
255 ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
256 ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
257 ak4376_write(AK4376_REG_PWR3, ak4376.low_mode ? 0x11 : 0x01);
258
259 /* Enable the master clock */
260 ak4376_set_mclk_freq(fsel, true);
261
262 /* Remember the frequency */
263 ak4376.fsel = fsel;
264}
265
266void audiohw_set_power_mode(int mode)
267{
268 /* This is handled via audiohw_set_frequency() since changing LPMODE
269 * bit requires power-down/power-up & changing other bits as well */
270 if(ak4376.low_mode != mode) {
271 ak4376.low_mode = mode;
272 audiohw_set_frequency(ak4376.fsel);
273 }
274}