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/cw2015.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/cw2015.c')
-rw-r--r-- | firmware/drivers/cw2015.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/firmware/drivers/cw2015.c b/firmware/drivers/cw2015.c new file mode 100644 index 0000000000..705ca16e22 --- /dev/null +++ b/firmware/drivers/cw2015.c | |||
@@ -0,0 +1,191 @@ | |||
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 "cw2015.h" | ||
23 | #include "i2c-async.h" | ||
24 | #include <string.h> | ||
25 | #include "system.h" | ||
26 | |||
27 | /* Headers for the debug menu */ | ||
28 | #ifndef BOOTLOADER | ||
29 | # include "action.h" | ||
30 | # include "list.h" | ||
31 | # include <stdio.h> | ||
32 | #endif | ||
33 | |||
34 | /* Battery profile info is an opaque blob. According to this, | ||
35 | * https://lore.kernel.org/linux-pm/20200503154855.duwj2djgqfiyleq5@earth.universe/T/#u | ||
36 | * the blob only comes from Cellwise testing a physical battery and cannot be | ||
37 | * obtained any other way. It's specific to a given battery so each target has | ||
38 | * its own profile. | ||
39 | * | ||
40 | * Profile data seems to be retained on the chip so it's not a hard requirement | ||
41 | * to define this. Provided you don't lose power in the meantime, it should be | ||
42 | * enough to just boot the OF, then boot Rockbox and read out the battery info | ||
43 | * from the CW2015 debug screen. | ||
44 | */ | ||
45 | #if defined(SHANLING_Q1) | ||
46 | static const uint8_t device_batinfo[CW2015_SIZE_BATINFO] = { | ||
47 | 0x15, 0x7E, 0x61, 0x59, 0x57, 0x55, 0x56, 0x4C, | ||
48 | 0x4E, 0x4D, 0x50, 0x4C, 0x45, 0x3A, 0x2D, 0x27, | ||
49 | 0x22, 0x1E, 0x19, 0x1E, 0x2A, 0x3C, 0x48, 0x45, | ||
50 | 0x1D, 0x94, 0x08, 0xF6, 0x15, 0x29, 0x48, 0x51, | ||
51 | 0x5D, 0x60, 0x63, 0x66, 0x45, 0x1D, 0x83, 0x38, | ||
52 | 0x09, 0x43, 0x16, 0x42, 0x76, 0x98, 0xA5, 0x1B, | ||
53 | 0x41, 0x76, 0x99, 0xBF, 0x80, 0xC0, 0xEF, 0xCB, | ||
54 | 0x2F, 0x00, 0x64, 0xA5, 0xB5, 0x0E, 0x30, 0x29, | ||
55 | }; | ||
56 | #else | ||
57 | # define NO_BATINFO | ||
58 | #endif | ||
59 | |||
60 | static uint8_t chip_batinfo[CW2015_SIZE_BATINFO]; | ||
61 | |||
62 | /* TODO: Finish implementing this | ||
63 | * | ||
64 | * Although this chip might give a better battery estimate than voltage does, | ||
65 | * the mainline linux driver has a lot of weird hacks due to oddities like the | ||
66 | * SoC getting stuck during charging, and from limited testing it seems this | ||
67 | * may occur for the Q1 too. | ||
68 | */ | ||
69 | |||
70 | static int cw2015_read_bat_info(uint8_t* data) | ||
71 | { | ||
72 | for(int i = 0; i < CW2015_SIZE_BATINFO; ++i) { | ||
73 | int r = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_BATINFO + i); | ||
74 | if(r < 0) | ||
75 | return r; | ||
76 | |||
77 | data[i] = r & 0xff; | ||
78 | } | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | void cw2015_init(void) | ||
84 | { | ||
85 | /* mdelay(100); */ | ||
86 | int rc = cw2015_read_bat_info(&chip_batinfo[0]); | ||
87 | if(rc < 0) | ||
88 | memset(chip_batinfo, 0, sizeof(chip_batinfo)); | ||
89 | } | ||
90 | |||
91 | int cw2015_get_vcell(void) | ||
92 | { | ||
93 | int vcell_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL); | ||
94 | int vcell_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL+1); | ||
95 | |||
96 | if(vcell_msb < 0 || vcell_lsb < 0) | ||
97 | return -1; | ||
98 | |||
99 | /* 14 bits, resolution 305 uV */ | ||
100 | int v_raw = ((vcell_msb & 0x3f) << 8) | vcell_lsb; | ||
101 | return v_raw * 61 / 200; | ||
102 | } | ||
103 | |||
104 | int cw2015_get_soc(void) | ||
105 | { | ||
106 | int soc_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_SOC); | ||
107 | |||
108 | if(soc_msb < 0) | ||
109 | return -1; | ||
110 | |||
111 | /* MSB is the state of charge in percentage. | ||
112 | * the LSB contains fractional information not useful to Rockbox. */ | ||
113 | return soc_msb & 0xff; | ||
114 | } | ||
115 | |||
116 | int cw2015_get_rrt(void) | ||
117 | { | ||
118 | int rrt_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT); | ||
119 | int rrt_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT+1); | ||
120 | |||
121 | if(rrt_msb < 0 || rrt_lsb < 0) | ||
122 | return -1; | ||
123 | |||
124 | /* 13 bits, resolution 1 minute */ | ||
125 | return ((rrt_msb & 0x1f) << 8) | rrt_lsb; | ||
126 | } | ||
127 | |||
128 | const uint8_t* cw2015_get_bat_info(void) | ||
129 | { | ||
130 | return &chip_batinfo[0]; | ||
131 | } | ||
132 | |||
133 | #ifndef BOOTLOADER | ||
134 | enum { | ||
135 | CW2015_DEBUG_VCELL = 0, | ||
136 | CW2015_DEBUG_SOC, | ||
137 | CW2015_DEBUG_RRT, | ||
138 | CW2015_DEBUG_BATINFO, | ||
139 | CW2015_DEBUG_BATINFO_LAST = CW2015_DEBUG_BATINFO + 7, | ||
140 | CW2015_DEBUG_NUM_ENTRIES, | ||
141 | }; | ||
142 | |||
143 | static int cw2015_debug_menu_cb(int action, struct gui_synclist* lists) | ||
144 | { | ||
145 | (void)lists; | ||
146 | |||
147 | if(action == ACTION_NONE) | ||
148 | action = ACTION_REDRAW; | ||
149 | |||
150 | return action; | ||
151 | } | ||
152 | |||
153 | static const char* cw2015_debug_menu_get_name(int item, void* data, | ||
154 | char* buf, size_t buflen) | ||
155 | { | ||
156 | (void)data; | ||
157 | |||
158 | /* hexdump of battery info */ | ||
159 | if(item >= CW2015_DEBUG_BATINFO && item <= CW2015_DEBUG_BATINFO_LAST) { | ||
160 | int i = item - CW2015_DEBUG_BATINFO; | ||
161 | const uint8_t* batinfo = cw2015_get_bat_info(); | ||
162 | snprintf(buf, buflen, "BatInfo%d: %02x %02x %02x %02x %02x %02x %02x %02x", i, | ||
163 | batinfo[8*i + 0], batinfo[8*i + 1], batinfo[8*i + 2], batinfo[8*i + 3], | ||
164 | batinfo[8*i + 4], batinfo[8*i + 5], batinfo[8*i + 6], batinfo[8*i + 7]); | ||
165 | return buf; | ||
166 | } | ||
167 | |||
168 | switch(item) { | ||
169 | case CW2015_DEBUG_VCELL: | ||
170 | snprintf(buf, buflen, "VCell: %d mV", cw2015_get_vcell()); | ||
171 | return buf; | ||
172 | case CW2015_DEBUG_SOC: | ||
173 | snprintf(buf, buflen, "SOC: %d%%", cw2015_get_soc()); | ||
174 | return buf; | ||
175 | case CW2015_DEBUG_RRT: | ||
176 | snprintf(buf, buflen, "Runtime: %d min", cw2015_get_rrt()); | ||
177 | return buf; | ||
178 | default: | ||
179 | return "---"; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | bool cw2015_debug_menu(void) | ||
184 | { | ||
185 | struct simplelist_info info; | ||
186 | simplelist_info_init(&info, "CW2015 debug", CW2015_DEBUG_NUM_ENTRIES, NULL); | ||
187 | info.action_callback = cw2015_debug_menu_cb; | ||
188 | info.get_name = cw2015_debug_menu_get_name; | ||
189 | return simplelist_show_list(&info); | ||
190 | } | ||
191 | #endif | ||