summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-23 17:30:58 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-13 22:01:33 +0100
commit4c60bc9e681865fcfc149775a1ed7ccd2613d5bf (patch)
tree99f8d91af2c171cf3843f0c14d41a20d9dc29c4f /firmware/drivers/audio
parent3abb7c5dd5be2ec6744bfc0a80967b20f1b59e30 (diff)
downloadrockbox-4c60bc9e681865fcfc149775a1ed7ccd2613d5bf.tar.gz
rockbox-4c60bc9e681865fcfc149775a1ed7ccd2613d5bf.zip
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
Diffstat (limited to 'firmware/drivers/audio')
-rw-r--r--firmware/drivers/audio/es9218.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/firmware/drivers/audio/es9218.c b/firmware/drivers/audio/es9218.c
new file mode 100644
index 0000000000..76d387221a
--- /dev/null
+++ b/firmware/drivers/audio/es9218.c
@@ -0,0 +1,226 @@
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 "system.h"
24#include "i2c-async.h"
25
26struct es9218_state {
27 enum es9218_clock_gear clk_gear;
28 uint32_t fsr;
29 uint32_t nco;
30};
31
32static struct es9218_state es9218;
33
34void es9218_open(void)
35{
36 /* Enable power supply */
37 es9218_set_power_pin(1);
38
39 /* "Wiggle" reset pin to get the internal oscillator to stabilize.
40 * This should also work if using an external powered oscillator,
41 * although in that case it's unnecessary to do this dance. */
42 es9218_set_reset_pin(1);
43 udelay(75);
44 es9218_set_reset_pin(0);
45 udelay(50);
46 es9218_set_reset_pin(1);
47 mdelay(2);
48
49 /* Initialize driver state */
50 es9218.clk_gear = ES9218_CLK_GEAR_1;
51 es9218.fsr = 0;
52 es9218.nco = 0;
53}
54
55void es9218_close(void)
56{
57 /* Turn off power supply */
58 es9218_set_power_pin(0);
59 es9218_set_reset_pin(0);
60}
61
62static void recalc_nco(void)
63{
64 /* nco * CLK *
65 * fsr = --------- *
66 * 2**32 */
67
68 uint32_t clk = es9218_get_mclk();
69 clk >>= (int)es9218.clk_gear;
70
71 uint64_t nco64 = es9218.fsr;
72 nco64 <<= 32;
73 nco64 /= clk;
74
75 /* let's just ignore overflow... */
76 uint32_t nco = nco64;
77 if(nco != es9218.nco) {
78 es9218.nco = nco;
79
80 /* registers must be written in this order */
81 es9218_write(ES9218_REG_PROG_NCO_BIT0_7, (nco >> 0) & 0xff);
82 es9218_write(ES9218_REG_PROG_NCO_BIT8_15, (nco >> 8) & 0xff);
83 es9218_write(ES9218_REG_PROG_NCO_BIT16_23, (nco >> 16) & 0xff);
84 es9218_write(ES9218_REG_PROG_NCO_BIT24_31, (nco >> 24) & 0xff);
85 }
86}
87
88void es9218_set_clock_gear(enum es9218_clock_gear gear)
89{
90 if(gear != es9218.clk_gear) {
91 es9218.clk_gear = gear;
92 es9218_update(ES9218_REG_SYSTEM, 0x0c, (uint8_t)(gear & 3) << 2);
93 recalc_nco();
94 }
95}
96
97void es9218_set_nco_frequency(uint32_t fsr)
98{
99 if(fsr != es9218.fsr) {
100 es9218.fsr = fsr;
101 recalc_nco();
102 }
103}
104
105void es9218_recompute_nco(void)
106{
107 recalc_nco();
108}
109
110void es9218_set_amp_mode(enum es9218_amp_mode mode)
111{
112 es9218_update(ES9218_REG_AMP_CONFIG, 0x03, (uint8_t)mode & 3);
113}
114
115void es9218_set_amp_powered(bool en)
116{
117 /* this doesn't seem to be necessary..? */
118 es9218_update(ES9218_REG_ANALOG_CTRL, 0x40, en ? 0x40 : 0x00);
119}
120
121void es9218_set_iface_role(enum es9218_iface_role role)
122{
123 /* asrc is used to lock onto the incoming audio frequency and is
124 * only used in aysnchronous slave mode. In synchronous operation,
125 * including master mode, it can be disabled to save power. */
126 int asrc_en = (role == ES9218_IFACE_ROLE_SLAVE ? 1 : 0);
127 int master_mode = (role == ES9218_IFACE_ROLE_MASTER ? 1 : 0);
128
129 es9218_update(ES9218_REG_MASTER_MODE_CONFIG, 1 << 7, master_mode << 7);
130 es9218_update(ES9218_REG_GENERAL_CONFIG, 1 << 7, asrc_en << 7);
131}
132
133void es9218_set_iface_format(enum es9218_iface_format fmt,
134 enum es9218_iface_bits bits)
135{
136 uint8_t val = 0;
137 val |= ((uint8_t)bits & 3) << 6;
138 val |= ((uint8_t)fmt & 3) << 4;
139 /* keep low 4 bits zero -> use normal I2S mode, disable DSD mode */
140 es9218_write(ES9218_REG_INPUT_SEL, val);
141}
142
143static int dig_vol_to_hw(int x)
144{
145 x = MIN(x, ES9218_DIG_VOLUME_MAX);
146 x = MAX(x, ES9218_DIG_VOLUME_MIN);
147 return 0xff - (x - ES9218_DIG_VOLUME_MIN) / ES9218_DIG_VOLUME_STEP;
148}
149
150static int amp_vol_to_hw(int x)
151{
152 x = MIN(x, ES9218_AMP_VOLUME_MAX);
153 x = MAX(x, ES9218_AMP_VOLUME_MIN);
154 return 24 - (x - ES9218_AMP_VOLUME_MIN) / ES9218_AMP_VOLUME_STEP;
155}
156
157void es9218_set_dig_volume(int vol_l, int vol_r)
158{
159 es9218_write(ES9218_REG_VOLUME_LEFT, dig_vol_to_hw(vol_l));
160 es9218_write(ES9218_REG_VOLUME_RIGHT, dig_vol_to_hw(vol_r));
161}
162
163void es9218_set_amp_volume(int vol)
164{
165 es9218_update(ES9218_REG_ANALOG_VOL, 0x1f, amp_vol_to_hw(vol));
166}
167
168void es9218_mute(bool en)
169{
170 es9218_update(ES9218_REG_FILTER_SYS_MUTE, 1, en ? 1 : 0);
171}
172
173void es9218_set_filter(enum es9218_filter_type filt)
174{
175 es9218_update(ES9218_REG_FILTER_SYS_MUTE, 0xe0, ((int)filt & 7) << 5);
176}
177
178void es9218_set_automute_time(int time)
179{
180 if(time < 0) time = 0;
181 if(time > 255) time = 255;
182 es9218_write(ES9218_REG_AUTOMUTE_TIME, time);
183}
184
185void es9218_set_automute_level(int dB)
186{
187 es9218_update(ES9218_REG_AUTOMUTE_LEVEL, 0x7f, dB);
188}
189
190void es9218_set_automute_fast_mode(bool en)
191{
192 es9218_update(ES9218_REG_MIX_AUTOMUTE, 0x10, en ? 0x10 : 0x00);
193}
194
195void es9218_set_dpll_bandwidth(int knob)
196{
197 es9218_update(ES9218_REG_ASRC_DPLL_BANDWIDTH, 0xf0, (knob & 0xf) << 4);
198}
199
200void es9218_set_thd_compensation(bool en)
201{
202 es9218_update(ES9218_REG_THD_COMP_BYPASS, 0x40, en ? 0x40 : 0);
203}
204
205void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3)
206{
207 es9218_write(ES9218_REG_THD_COMP_C2_LO, c2 & 0xff);
208 es9218_write(ES9218_REG_THD_COMP_C2_HI, (c2 >> 8) & 0xff);
209 es9218_write(ES9218_REG_THD_COMP_C3_LO, c3 & 0xff);
210 es9218_write(ES9218_REG_THD_COMP_C3_HI, (c3 >> 8) & 0xff);
211}
212
213int es9218_read(int reg)
214{
215 return i2c_reg_read1(ES9218_BUS, ES9218_ADDR, reg);
216}
217
218void es9218_write(int reg, uint8_t val)
219{
220 i2c_reg_write1(ES9218_BUS, ES9218_ADDR, reg, val);
221}
222
223void es9218_update(int reg, uint8_t msk, uint8_t val)
224{
225 i2c_reg_modify1(ES9218_BUS, ES9218_ADDR, reg, msk, val, NULL);
226}