diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-23 17:30:58 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-07-13 22:01:33 +0100 |
commit | 4c60bc9e681865fcfc149775a1ed7ccd2613d5bf (patch) | |
tree | 99f8d91af2c171cf3843f0c14d41a20d9dc29c4f /firmware/drivers/audio/es9218.c | |
parent | 3abb7c5dd5be2ec6744bfc0a80967b20f1b59e30 (diff) | |
download | rockbox-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/es9218.c')
-rw-r--r-- | firmware/drivers/audio/es9218.c | 226 |
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 | |||
26 | struct es9218_state { | ||
27 | enum es9218_clock_gear clk_gear; | ||
28 | uint32_t fsr; | ||
29 | uint32_t nco; | ||
30 | }; | ||
31 | |||
32 | static struct es9218_state es9218; | ||
33 | |||
34 | void 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 | |||
55 | void es9218_close(void) | ||
56 | { | ||
57 | /* Turn off power supply */ | ||
58 | es9218_set_power_pin(0); | ||
59 | es9218_set_reset_pin(0); | ||
60 | } | ||
61 | |||
62 | static 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 | |||
88 | void 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 | |||
97 | void es9218_set_nco_frequency(uint32_t fsr) | ||
98 | { | ||
99 | if(fsr != es9218.fsr) { | ||
100 | es9218.fsr = fsr; | ||
101 | recalc_nco(); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | void es9218_recompute_nco(void) | ||
106 | { | ||
107 | recalc_nco(); | ||
108 | } | ||
109 | |||
110 | void es9218_set_amp_mode(enum es9218_amp_mode mode) | ||
111 | { | ||
112 | es9218_update(ES9218_REG_AMP_CONFIG, 0x03, (uint8_t)mode & 3); | ||
113 | } | ||
114 | |||
115 | void 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 | |||
121 | void 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 | |||
133 | void 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 | |||
143 | static 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 | |||
150 | static 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 | |||
157 | void 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 | |||
163 | void es9218_set_amp_volume(int vol) | ||
164 | { | ||
165 | es9218_update(ES9218_REG_ANALOG_VOL, 0x1f, amp_vol_to_hw(vol)); | ||
166 | } | ||
167 | |||
168 | void es9218_mute(bool en) | ||
169 | { | ||
170 | es9218_update(ES9218_REG_FILTER_SYS_MUTE, 1, en ? 1 : 0); | ||
171 | } | ||
172 | |||
173 | void es9218_set_filter(enum es9218_filter_type filt) | ||
174 | { | ||
175 | es9218_update(ES9218_REG_FILTER_SYS_MUTE, 0xe0, ((int)filt & 7) << 5); | ||
176 | } | ||
177 | |||
178 | void 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 | |||
185 | void es9218_set_automute_level(int dB) | ||
186 | { | ||
187 | es9218_update(ES9218_REG_AUTOMUTE_LEVEL, 0x7f, dB); | ||
188 | } | ||
189 | |||
190 | void es9218_set_automute_fast_mode(bool en) | ||
191 | { | ||
192 | es9218_update(ES9218_REG_MIX_AUTOMUTE, 0x10, en ? 0x10 : 0x00); | ||
193 | } | ||
194 | |||
195 | void es9218_set_dpll_bandwidth(int knob) | ||
196 | { | ||
197 | es9218_update(ES9218_REG_ASRC_DPLL_BANDWIDTH, 0xf0, (knob & 0xf) << 4); | ||
198 | } | ||
199 | |||
200 | void es9218_set_thd_compensation(bool en) | ||
201 | { | ||
202 | es9218_update(ES9218_REG_THD_COMP_BYPASS, 0x40, en ? 0x40 : 0); | ||
203 | } | ||
204 | |||
205 | void 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 | |||
213 | int es9218_read(int reg) | ||
214 | { | ||
215 | return i2c_reg_read1(ES9218_BUS, ES9218_ADDR, reg); | ||
216 | } | ||
217 | |||
218 | void es9218_write(int reg, uint8_t val) | ||
219 | { | ||
220 | i2c_reg_write1(ES9218_BUS, ES9218_ADDR, reg, val); | ||
221 | } | ||
222 | |||
223 | void es9218_update(int reg, uint8_t msk, uint8_t val) | ||
224 | { | ||
225 | i2c_reg_modify1(ES9218_BUS, ES9218_ADDR, reg, msk, val, NULL); | ||
226 | } | ||