From 4c60bc9e681865fcfc149775a1ed7ccd2613d5bf Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sun, 23 May 2021 17:30:58 +0100 Subject: 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 --- firmware/drivers/cw2015.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 firmware/drivers/cw2015.c (limited to 'firmware/drivers/cw2015.c') 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "cw2015.h" +#include "i2c-async.h" +#include +#include "system.h" + +/* Headers for the debug menu */ +#ifndef BOOTLOADER +# include "action.h" +# include "list.h" +# include +#endif + +/* Battery profile info is an opaque blob. According to this, + * https://lore.kernel.org/linux-pm/20200503154855.duwj2djgqfiyleq5@earth.universe/T/#u + * the blob only comes from Cellwise testing a physical battery and cannot be + * obtained any other way. It's specific to a given battery so each target has + * its own profile. + * + * Profile data seems to be retained on the chip so it's not a hard requirement + * to define this. Provided you don't lose power in the meantime, it should be + * enough to just boot the OF, then boot Rockbox and read out the battery info + * from the CW2015 debug screen. + */ +#if defined(SHANLING_Q1) +static const uint8_t device_batinfo[CW2015_SIZE_BATINFO] = { + 0x15, 0x7E, 0x61, 0x59, 0x57, 0x55, 0x56, 0x4C, + 0x4E, 0x4D, 0x50, 0x4C, 0x45, 0x3A, 0x2D, 0x27, + 0x22, 0x1E, 0x19, 0x1E, 0x2A, 0x3C, 0x48, 0x45, + 0x1D, 0x94, 0x08, 0xF6, 0x15, 0x29, 0x48, 0x51, + 0x5D, 0x60, 0x63, 0x66, 0x45, 0x1D, 0x83, 0x38, + 0x09, 0x43, 0x16, 0x42, 0x76, 0x98, 0xA5, 0x1B, + 0x41, 0x76, 0x99, 0xBF, 0x80, 0xC0, 0xEF, 0xCB, + 0x2F, 0x00, 0x64, 0xA5, 0xB5, 0x0E, 0x30, 0x29, +}; +#else +# define NO_BATINFO +#endif + +static uint8_t chip_batinfo[CW2015_SIZE_BATINFO]; + +/* TODO: Finish implementing this + * + * Although this chip might give a better battery estimate than voltage does, + * the mainline linux driver has a lot of weird hacks due to oddities like the + * SoC getting stuck during charging, and from limited testing it seems this + * may occur for the Q1 too. + */ + +static int cw2015_read_bat_info(uint8_t* data) +{ + for(int i = 0; i < CW2015_SIZE_BATINFO; ++i) { + int r = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_BATINFO + i); + if(r < 0) + return r; + + data[i] = r & 0xff; + } + + return 0; +} + +void cw2015_init(void) +{ + /* mdelay(100); */ + int rc = cw2015_read_bat_info(&chip_batinfo[0]); + if(rc < 0) + memset(chip_batinfo, 0, sizeof(chip_batinfo)); +} + +int cw2015_get_vcell(void) +{ + int vcell_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL); + int vcell_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL+1); + + if(vcell_msb < 0 || vcell_lsb < 0) + return -1; + + /* 14 bits, resolution 305 uV */ + int v_raw = ((vcell_msb & 0x3f) << 8) | vcell_lsb; + return v_raw * 61 / 200; +} + +int cw2015_get_soc(void) +{ + int soc_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_SOC); + + if(soc_msb < 0) + return -1; + + /* MSB is the state of charge in percentage. + * the LSB contains fractional information not useful to Rockbox. */ + return soc_msb & 0xff; +} + +int cw2015_get_rrt(void) +{ + int rrt_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT); + int rrt_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT+1); + + if(rrt_msb < 0 || rrt_lsb < 0) + return -1; + + /* 13 bits, resolution 1 minute */ + return ((rrt_msb & 0x1f) << 8) | rrt_lsb; +} + +const uint8_t* cw2015_get_bat_info(void) +{ + return &chip_batinfo[0]; +} + +#ifndef BOOTLOADER +enum { + CW2015_DEBUG_VCELL = 0, + CW2015_DEBUG_SOC, + CW2015_DEBUG_RRT, + CW2015_DEBUG_BATINFO, + CW2015_DEBUG_BATINFO_LAST = CW2015_DEBUG_BATINFO + 7, + CW2015_DEBUG_NUM_ENTRIES, +}; + +static int cw2015_debug_menu_cb(int action, struct gui_synclist* lists) +{ + (void)lists; + + if(action == ACTION_NONE) + action = ACTION_REDRAW; + + return action; +} + +static const char* cw2015_debug_menu_get_name(int item, void* data, + char* buf, size_t buflen) +{ + (void)data; + + /* hexdump of battery info */ + if(item >= CW2015_DEBUG_BATINFO && item <= CW2015_DEBUG_BATINFO_LAST) { + int i = item - CW2015_DEBUG_BATINFO; + const uint8_t* batinfo = cw2015_get_bat_info(); + snprintf(buf, buflen, "BatInfo%d: %02x %02x %02x %02x %02x %02x %02x %02x", i, + batinfo[8*i + 0], batinfo[8*i + 1], batinfo[8*i + 2], batinfo[8*i + 3], + batinfo[8*i + 4], batinfo[8*i + 5], batinfo[8*i + 6], batinfo[8*i + 7]); + return buf; + } + + switch(item) { + case CW2015_DEBUG_VCELL: + snprintf(buf, buflen, "VCell: %d mV", cw2015_get_vcell()); + return buf; + case CW2015_DEBUG_SOC: + snprintf(buf, buflen, "SOC: %d%%", cw2015_get_soc()); + return buf; + case CW2015_DEBUG_RRT: + snprintf(buf, buflen, "Runtime: %d min", cw2015_get_rrt()); + return buf; + default: + return "---"; + } +} + +bool cw2015_debug_menu(void) +{ + struct simplelist_info info; + simplelist_info_init(&info, "CW2015 debug", CW2015_DEBUG_NUM_ENTRIES, NULL); + info.action_callback = cw2015_debug_menu_cb; + info.get_name = cw2015_debug_menu_get_name; + return simplelist_show_list(&info); +} +#endif -- cgit v1.2.3