summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDana Conrad <dconrad@fastmail.com>2024-10-13 21:19:42 +0000
committerSolomon Peachy <pizza@shaftnet.org>2024-11-22 17:01:39 -0500
commit253eb79db39f8d5bf88ec1d243e2668b7c9479c3 (patch)
tree959d4f3300c02129cec4837e05e6c5f1c0d82cb5
parentd7b57e33d921f6a486dafa0987df7dd485507071 (diff)
downloadrockbox-253eb79db3.tar.gz
rockbox-253eb79db3.zip
erosqnative: hw4 support
Support hw4 units with AXP2101 PMU Bootloader successfully compiles and loads onto device. The LCD appears to be identical to hw3 units. Scroll wheel and buttons work Audio output works, including volume. HP/LO detect works Rockbox build is generic GPIO gating logic seems to be working as intended now. - Added new GPIO definitions - some significant overlaps with pins from previous hardware revisions... - Added some GPIO definitions for older players we didn't know about - Add register definitions for AXP2101 from datasheet (these are very different from AXP192!) - Add AXP2101 regulator definitions, need to support multiple step sizes per regulator. - Verify AXP2101 voltage set multi-range logic - Verify AXP2101 voltage get multi-range logic - Make AXP2101 its own driver - AXP2101 driver should be "minimally viable", though I think there is some extra functionality that could be implemented. - Disabling the coulomb counter stuff - we could maybe make the E-Gauge work for the same purpose, but it only appears to be used on the debug screen at the moment so it doesn't seem like it's worth the effort. - Found new button GPIOs - Found error in my GPIO setting logic, blue light works now! - Set LDO/DCDC output voltages to OF's settings, as far as I can tell. - Determined we probably want TCS1421_CFG1:0 to be 0x00, for UFP behavior - Tested this rb build with both old and new bootloaders on hw1.5, hw2, hw4 in as many configurations as I can think of, works across the board. - Bootloader can install itself on hw4, so nand chip isn't novel - Uninstallation file can be made by patcher script, works on hw4 - Installation file can be made by patcher script, works on hw4 - Added HW4 to rbutil, manual Change-Id: I5b75782273e81c2c6f2b9c79501c8b7cbf88391f
-rw-r--r--firmware/SOURCES3
-rw-r--r--firmware/drivers/axp-2101.c661
-rw-r--r--firmware/drivers/axp-pmu.c55
-rw-r--r--firmware/export/axp-2101.h211
-rw-r--r--firmware/export/config/erosqnative.h1
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c128
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h139
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c15
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c179
-rw-r--r--firmware/target/mips/ingenic_x1000/gpio-x1000.c32
-rw-r--r--manual/getting_started/hibyos_nativeinstall.tex7
-rw-r--r--manual/rockbox_interface/main.tex15
-rwxr-xr-xtools/configure35
-rw-r--r--utils/rbutilqt/rbutil.ini42
14 files changed, 1409 insertions, 114 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 93d62ec8cb..ae0fed0efe 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1976,6 +1976,9 @@ drivers/i2c-async.c
#ifdef HAVE_AXP_PMU
drivers/axp-pmu.c
#endif
+#ifdef HAVE_AXP2101_ADDON
+drivers/axp-2101.c
+#endif
#ifdef HAVE_FT6x06
drivers/ft6x06.c
#endif
diff --git a/firmware/drivers/axp-2101.c b/firmware/drivers/axp-2101.c
new file mode 100644
index 0000000000..74a302f1b7
--- /dev/null
+++ b/firmware/drivers/axp-2101.c
@@ -0,0 +1,661 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "axp-2101.h"
+#include "power.h"
+#include "system.h"
+#include "i2c-async.h"
+#include <string.h>
+
+/* Headers for the debug menu */
+#ifndef BOOTLOADER
+# include "action.h"
+# include "list.h"
+# include <stdio.h>
+#endif
+
+struct axp_adc_info {
+ uint8_t reg;
+ uint8_t en_reg;
+ uint8_t en_bit;
+ int8_t num;
+ int8_t den;
+};
+
+struct axp_supply_info {
+ uint8_t volt_reg;
+ uint8_t volt_reg_mask;
+ uint8_t en_reg;
+ uint8_t en_bit;
+ int min_mV;
+ int max_mV; // if multiple steps, set to max of step 1
+ int step_mV;
+ int step2_min_mV;
+ int step2_mV;
+ int step2_max_mV;
+ int step3_min_mV;
+ int step3_mV;
+ int step3_max_mV;
+};
+
+static const struct axp_adc_info axp_adc_info[AXP2101_NUM_ADC_CHANNELS] = {
+ // TODO: Datasheet ADC conversion table doesn't seem to make any sense...
+ // 0x000 0x001 0x002 ... 0xFFF
+ // 0mV 1mV 2mV ... 8.192V
+ [AXP2101_ADC_VBAT_VOLTAGE] = {AXP2101_REG_ADC_VBAT_H, AXP2101_REG_ADCCHNENABLE, 1 << 0, 1, 1},
+ // 0mV 1mV 2mV ... 8.192V
+ [AXP2101_ADC_VBUS_VOLTAGE] = {AXP2101_REG_ADC_VBUS_H, AXP2101_REG_ADCCHNENABLE, 1 << 2, 1, 1},
+ // 0mV 1mV 2mV ... 8.192V
+ [AXP2101_ADC_VSYS_VOLTAGE] = {AXP2101_REG_ADC_VSYS_H, AXP2101_REG_ADCCHNENABLE, 1 << 3, 1, 1},
+ // 0mV 0.5mV 1mV ... 4.096V
+ [AXP2101_ADC_TS_VOLTAGE] = {AXP2101_REG_ADC_TS_H, AXP2101_REG_ADCCHNENABLE, 1 << 1, 1, 1},
+ // 0mV 0.1mV 2mV ... 0.8192V
+ [AXP2101_ADC_DIE_TEMPERATURE] = {AXP2101_REG_ADC_TDIE_H, AXP2101_REG_ADCCHNENABLE, 1 << 4, 1, 1},
+};
+
+static const struct axp_supply_info axp_supply_info[AXP2101_NUM_SUPPLIES] = {
+ [AXP2101_SUPPLY_DCDC1] = {
+ .volt_reg = 0x82,
+ .volt_reg_mask = 0x1f, // N.B. max value 0b10011, values higher reserved
+ .en_reg = 0x80,
+ .en_bit = 0,
+ .min_mV = 1500,
+ .max_mV = 3400,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_DCDC2] = {
+ .volt_reg = 0x83,
+ .volt_reg_mask = 0x7f, // N.B. max value 0b1010111, values higher reserved
+ .en_reg = 0x80,
+ .en_bit = 1,
+ .min_mV = 500,
+ .max_mV = 1200,
+ .step_mV = 10,
+ .step2_min_mV = 1220,
+ .step2_mV = 20,
+ .step2_max_mV = 1540,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ // N.B. 10mV/step from 0.5 - 1.2v (71 steps),
+ // 20mV/step from 1.22 - 1.54v (17 steps)
+ },
+ [AXP2101_SUPPLY_DCDC3] = {
+ .volt_reg = 0x84,
+ .volt_reg_mask = 0x7f, // N.B. max value 0b1101011, values higher reserved
+ .en_reg = 0x80,
+ .en_bit = 2,
+ .min_mV = 500,
+ .max_mV = 1200,
+ .step_mV = 10,
+ .step2_min_mV = 1220,
+ .step2_mV = 20,
+ .step2_max_mV = 1540,
+ .step3_min_mV = 1600,
+ .step3_mV = 100,
+ .step3_max_mV = 3400,
+ // N.B. 10mV/step from 0.5 - 1.2V (71 steps)
+ // 20mV/step from 1.22 - 1.54V (17 steps)
+ // 100mV/step from 1.6 - 3.4V (19 steps)
+ },
+ [AXP2101_SUPPLY_DCDC4] = {
+ .volt_reg = 0x85,
+ .volt_reg_mask = 0x7f, // N.B. max value 0b1100110, values higher reserved
+ .en_reg = 0x80,
+ .en_bit = 3,
+ .min_mV = 500,
+ .max_mV = 1200,
+ .step_mV = 10,
+ .step2_min_mV = 1220,
+ .step2_mV = 20,
+ .step2_max_mV = 1840,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ // N.B. 10mV/step from 0.5 - 1.2V (71 steps)
+ // 20mV/step from 1.22 - 1.84V (32 steps)
+ },
+ [AXP2101_SUPPLY_DCDC5] = {
+ .volt_reg = 0x86,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b10111, values higher reserved
+ .en_reg = 0x80,
+ .en_bit = 4,
+ .min_mV = 1400,
+ .max_mV = 3700,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_ALDO1] = {
+ .volt_reg = 0x92,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b1110, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 0,
+ .min_mV = 500,
+ .max_mV = 3500,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_ALDO2] = {
+ .volt_reg = 0x93,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b1110, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 1,
+ .min_mV = 500,
+ .max_mV = 3500,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_ALDO3] = {
+ .volt_reg = 0x94,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b1110, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 2,
+ .min_mV = 500,
+ .max_mV = 3500,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_ALDO4] = {
+ .volt_reg = 0x95,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b1110, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 3,
+ .min_mV = 500,
+ .max_mV = 3500,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_BLDO1] = {
+ .volt_reg = 0x96,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b11110, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 4,
+ .min_mV = 500,
+ .max_mV = 3500,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_BLDO2] = {
+ .volt_reg = 0x97,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b11110, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 5,
+ .min_mV = 500,
+ .max_mV = 3500,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_DLDO1] = {
+ .volt_reg = 0x99,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b11100, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 7,
+ .min_mV = 500,
+ .max_mV = 3400,
+ .step_mV = 100,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_DLDO2] = {
+ .volt_reg = 0x9a,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b11100, values higher reserved
+ .en_reg = 0x91,
+ .en_bit = 0,
+ .min_mV = 500,
+ .max_mV = 1400,
+ .step_mV = 560,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ [AXP2101_SUPPLY_VCPUS] = {
+ .volt_reg = 0x98,
+ .volt_reg_mask = 0x3f, // N.B. max value 0b10011, values higher reserved
+ .en_reg = 0x90,
+ .en_bit = 6,
+ .min_mV = 500,
+ .max_mV = 1400,
+ .step_mV = 50,
+ .step2_min_mV = 0,
+ .step2_mV = 0,
+ .step2_max_mV = 0,
+ .step3_min_mV = 0,
+ .step3_mV = 0,
+ .step3_max_mV = 0,
+ },
+ // No voltage reg given - are these fixed?
+ // [AXP_SUPPLY_RTCLDO1] = {
+ // },
+ // [AXP_SUPPLY_RTCLDO2] = {
+ // },
+};
+
+void axp2101_init(void)
+{
+}
+
+void axp2101_supply_set_voltage(int supply, int voltage)
+{
+ const struct axp_supply_info* info = &axp_supply_info[supply];
+ if(info->volt_reg == 0 || info->volt_reg_mask == 0)
+ return;
+
+ if(voltage > 0 && info->step_mV != 0) {
+ if(voltage < info->min_mV)
+ return;
+
+ int regval;
+
+ // there's probably a more elegant way to do this...
+ if(voltage > info->max_mV) {
+ if(info->step2_max_mV == 0) {
+ return;
+ } else {
+ if(voltage > info->step2_max_mV) {
+ if(info->step3_max_mV == 0 || voltage > info->step3_max_mV) {
+ return;
+ } else {
+ // step3 range
+ regval = ((info->max_mV - info->min_mV) / info->step_mV)\
+ + ((info->step2_max_mV - info->step2_min_mV) / info->step2_mV)\
+ + ((voltage - info->step3_min_mV) / info->step3_mV) + 2;
+ }
+ } else {
+ // step2 range
+ regval = ((info->max_mV - info->min_mV) / info->step_mV)\
+ + ((voltage - info->step2_min_mV) / info->step2_mV) + 1;
+ }
+ }
+ } else {
+ // step1 range
+ regval = (voltage - info->min_mV) / info->step_mV;
+ }
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg,
+ info->volt_reg_mask, regval, NULL);
+ }
+
+ if(info->en_bit != 0xff) {
+ i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ info->en_reg, info->en_bit,
+ voltage > 0 ? 1 : 0, NULL);
+ }
+}
+
+int axp2101_supply_get_voltage(int supply)
+{
+ const struct axp_supply_info* info = &axp_supply_info[supply];
+ if(info->volt_reg == 0)
+ return AXP2101_SUPPLY_NOT_PRESENT;
+
+ if(info->en_reg != 0) {
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->en_reg);
+ if(r < 0)
+ return AXP2101_SUPPLY_DISABLED;
+ if(r & (1 << info->en_bit) == 0)
+ return AXP2101_SUPPLY_DISABLED;
+ }
+
+ /* Hack, avoid undefined shift below. Can be useful too... */
+ if(info->volt_reg_mask == 0)
+ return info->min_mV;
+
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, info->volt_reg);
+ if(r < 0)
+ return 0;
+
+ int val;
+ r = r & info->volt_reg_mask;
+
+ // there's probably a more elegant way to do this...
+ if(r > ((info->max_mV - info->min_mV) / info->step_mV)) {
+ r = r - ((info->max_mV - info->min_mV) / info->step_mV);
+
+ if(r > ((info->step2_max_mV - info->step2_min_mV) / info->step2_mV + 1)) {
+ r = r - ((info->step2_max_mV - info->step2_min_mV) / info->step2_mV);
+ /* step 3 */
+ val = info->step3_min_mV + ((r-2) * info->step3_mV);
+
+ } else {
+ /* step 2 */
+ val = info->step2_min_mV + ((r-1) * info->step2_mV);
+
+ }
+ } else {
+ /* step 1 */
+ val = info->min_mV + (r * info->step_mV);
+
+ }
+
+ return val;
+}
+
+/* TODO: can we trust the battery current direction? */
+int axp2101_battery_status(void)
+{
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP2101_REG_PMU_STATUS2);
+ if((r >> 5) & 0x03 == 0) {
+ return AXP2101_BATT_FULL;
+ } else if((r >> 5) & 0x03 == 01) {
+ return AXP2101_BATT_CHARGING;
+ } else {
+ return AXP2101_BATT_DISCHARGING;
+ }
+}
+
+int axp2101_input_status(void)
+{
+#ifdef HAVE_BATTERY_SWITCH
+ int input_status = 0;
+#else
+ int input_status = AXP2101_INPUT_BATTERY;
+#endif
+
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP2101_REG_PMU_STATUS1);
+ if(r & 0x20)
+ input_status |= AXP2101_INPUT_USB;
+#ifdef HAVE_BATTERY_SWITCH
+ if(r & 0x80)
+ input_status |= AXP2101_INPUT_BATTERY;
+#endif
+
+ return input_status;
+}
+
+int axp2101_adc_read(int adc)
+{
+ int value = axp2101_adc_read_raw(adc);
+ if(value == INT_MIN)
+ return INT_MIN;
+
+ return axp2101_adc_conv_raw(adc, value);
+}
+
+int axp2101_adc_read_raw(int adc)
+{
+ /* Read the ADC */
+ uint8_t buf[2];
+ uint8_t reg = axp_adc_info[adc].reg;
+ int rc = i2c_reg_read(AXP_PMU_BUS, AXP_PMU_ADDR, reg, 2, &buf[0]);
+ if(rc != I2C_STATUS_OK)
+ return INT_MIN;
+
+ /* Parse the value */
+ return ((buf[0] & 0x3f) << 8) | (buf[1] & 0xff);
+}
+
+int axp2101_adc_conv_raw(int adc, int value)
+{
+ return axp_adc_info[adc].num * value / axp_adc_info[adc].den;
+}
+
+void axp2101_adc_set_enabled(int adc_bits)
+{
+ uint8_t xfer[1];
+ xfer[0] = 0;
+
+ /* Compute the new register values */
+ const struct axp_adc_info* info = axp_adc_info;
+ for(int i = 0; i < AXP2101_NUM_ADC_CHANNELS; ++i) {
+ if(!(adc_bits & (1 << i)))
+ continue;
+
+ xfer[0] |= info[i].en_bit;
+ }
+
+ /* Update the configuration */
+ i2c_reg_write(AXP_PMU_BUS, AXP_PMU_ADDR, AXP2101_REG_ADCCHNENABLE, 1, &xfer[0]);
+}
+
+// TODO: See if we can figure out "optimum" battery chemistry-type settings
+// like constant-current charging, charge curves... that stuff is all configurable
+// as far as I can tell! Probably important to at least figure out if the defaults
+// are clearly wrong or not!
+
+// TODO: what are DATA_BUFFER 0-3 for????
+
+// there are many current settings:
+// Reg 16: Input current limit control
+// Reg 61: Precharge current limit
+// Reg 62: Constant current charge current limit
+// Reg 63: Charging termination current limit
+
+// there are also voltage settings for charging:
+// Reg 14: Linear Charger Vsys voltage dpm
+// Reg 15: Input Voltage limit control
+// Reg 64: CV charger charge voltage limit
+
+// There are also some timer stuff:
+// Reg 67: Charger timeout setting and control
+
+// constant current charge current limits
+static const int chargecurrent_tbl[] = {
+ 0, 25, 50, 75, 100, 125, 150, 175, 200,
+ 300, 400, 500, 600, 700, 800, 900, 1000,
+};
+
+// constant current charge current limits
+void axp2101_set_charge_current(int current_mA)
+{
+ /* find greatest charging current not exceeding requested current */
+ unsigned int index = 0;
+ while(index < ARRAYLEN(chargecurrent_tbl)-1 &&
+ chargecurrent_tbl[index+1] <= current_mA)
+ ++index;
+
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_ICC_SETTING, 0x0f, index, NULL);
+}
+
+int axp2101_get_charge_current(void)
+{
+ int ret = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_ICC_SETTING);
+ if(ret < 0)
+ ret = 0;
+
+ return chargecurrent_tbl[ret & 0x0f];
+}
+
+void axp2101_power_off(void)
+{
+ /* Set the shutdown bit */
+ i2c_reg_setbit1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_PMUCOMMCONFIG, 0, 1, NULL);
+}
+
+#ifndef BOOTLOADER
+enum {
+ AXP_DEBUG_BATTERY_STATUS,
+ AXP_DEBUG_INPUT_STATUS,
+ AXP_DEBUG_CHARGE_CURRENT,
+ AXP_DEBUG_FIRST_ADC,
+ AXP_DEBUG_FIRST_SUPPLY = AXP_DEBUG_FIRST_ADC + AXP2101_NUM_ADC_CHANNELS,
+ AXP_DEBUG_NUM_ENTRIES = AXP_DEBUG_FIRST_SUPPLY + AXP2101_NUM_SUPPLIES,
+};
+
+static int axp2101_debug_menu_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+
+ if(action == ACTION_NONE)
+ action = ACTION_REDRAW;
+
+ return action;
+}
+
+static const char* axp2101_debug_menu_get_name(int item, void* data,
+ char* buf, size_t buflen)
+{
+ (void)data;
+
+ static const char* const adc_names[] = {
+ "V_bat", "V_bus", "V_sys", "V_ts", "V_die",
+ };
+
+ static const char* const adc_units[] = {
+ "mV", "mV", "mV", "mV", "C*100",
+ };
+
+ static const char* const supply_names[] = {
+ "DCDC1", "DCDC2", "DCDC3", "DCDC4", "DCDC5",
+ "ALDO1", "ALDO2", "ALDO3", "ALDO4", "BLDO1", "BLDO2", "DLDO1", "DLDO2",
+ "VCPUS",
+ };
+
+ int adc = item - AXP_DEBUG_FIRST_ADC;
+ if(item >= AXP_DEBUG_FIRST_ADC && adc < AXP2101_NUM_ADC_CHANNELS) {
+ int raw_value = axp2101_adc_read_raw(adc);
+ if(raw_value == INT_MIN) {
+ snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
+ return buf;
+ }
+
+ int value = axp2101_adc_conv_raw(adc, raw_value);
+ snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
+ value, adc_units[adc]);
+
+ return buf;
+ }
+
+ int supply = item - AXP_DEBUG_FIRST_SUPPLY;
+ if(item >= AXP_DEBUG_FIRST_SUPPLY && supply < AXP2101_NUM_SUPPLIES) {
+ int voltage = axp2101_supply_get_voltage(supply);
+ if(voltage == AXP2101_SUPPLY_NOT_PRESENT)
+ snprintf(buf, buflen, "%s: [Not Present]", supply_names[supply]);
+ else if(voltage == AXP2101_SUPPLY_DISABLED)
+ snprintf(buf, buflen, "%s: [Disabled]", supply_names[supply]);
+ else
+ snprintf(buf, buflen, "%s: %d mV", supply_names[supply], voltage);
+
+ return buf;
+ }
+
+ switch(item) {
+ case AXP_DEBUG_BATTERY_STATUS: {
+ switch(axp2101_battery_status()) {
+ case AXP2101_BATT_FULL:
+ return "Battery: Full";
+ case AXP2101_BATT_CHARGING:
+ return "Battery: Charging";
+ case AXP2101_BATT_DISCHARGING:
+ return "Battery: Discharging";
+ default:
+ return "Battery: Unknown";
+ }
+ } break;
+
+ case AXP_DEBUG_INPUT_STATUS: {
+ int s = axp2101_input_status();
+ const char* ac = (s & AXP2101_INPUT_AC) ? " AC" : "";
+ const char* usb = (s & AXP2101_INPUT_USB) ? " USB" : "";
+ const char* batt = (s & AXP2101_INPUT_BATTERY) ? " Battery" : "";
+ snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
+ return buf;
+ } break;
+
+ case AXP_DEBUG_CHARGE_CURRENT: {
+ int current = axp2101_get_charge_current();
+ snprintf(buf, buflen, "Max charge current: %d mA", current);
+ return buf;
+ } break;
+
+ default:
+ return "---";
+ }
+}
+
+bool axp2101_debug_menu(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "AXP debug", AXP_DEBUG_NUM_ENTRIES, NULL);
+ info.action_callback = axp2101_debug_menu_cb;
+ info.get_name = axp2101_debug_menu_get_name;
+ return simplelist_show_list(&info);
+}
+#endif /* !BOOTLOADER */
+
+/* This is basically the only valid implementation, so define it here */
+unsigned int axp2101_power_input_status(void)
+{
+ unsigned int state = 0;
+ int input_status = axp2101_input_status();
+
+ if(input_status & AXP2101_INPUT_AC)
+ state |= POWER_INPUT_MAIN_CHARGER;
+
+ if(input_status & AXP2101_INPUT_USB)
+ state |= POWER_INPUT_USB_CHARGER;
+
+#ifdef HAVE_BATTERY_SWITCH
+ if(input_status & AXP2101_INPUT_BATTERY)
+ state |= POWER_INPUT_BATTERY;
+#endif
+
+ return state;
+}
diff --git a/firmware/drivers/axp-pmu.c b/firmware/drivers/axp-pmu.c
index d59fbb2e3f..0aa1610623 100644
--- a/firmware/drivers/axp-pmu.c
+++ b/firmware/drivers/axp-pmu.c
@@ -25,6 +25,11 @@
#include "i2c-async.h"
#include <string.h>
+#if defined(HAVE_AXP2101_ADDON)
+# include "axp-2101.h"
+# include "devicedata.h"
+#endif
+
/* Headers for the debug menu */
#ifndef BOOTLOADER
# include "action.h"
@@ -521,11 +526,24 @@ static const char* axp_debug_menu_get_name(int item, void* data,
bool axp_debug_menu(void)
{
- struct simplelist_info info;
- simplelist_info_init(&info, "AXP debug", AXP_DEBUG_NUM_ENTRIES, NULL);
- info.action_callback = axp_debug_menu_cb;
- info.get_name = axp_debug_menu_get_name;
- return simplelist_show_list(&info);
+#if defined(EROS_QN)
+ int devicever;
+# if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+# else
+ devicever = device_data.lcd_version;
+# endif
+ if (devicever >= 4) {
+ return axp2101_debug_menu();
+ } else
+#endif
+ {
+ struct simplelist_info info;
+ simplelist_info_init(&info, "AXP debug", AXP_DEBUG_NUM_ENTRIES, NULL);
+ info.action_callback = axp_debug_menu_cb;
+ info.get_name = axp_debug_menu_get_name;
+ return simplelist_show_list(&info);
+ }
}
#endif /* !BOOTLOADER */
@@ -533,18 +551,31 @@ bool axp_debug_menu(void)
unsigned int power_input_status(void)
{
unsigned int state = 0;
- int input_status = axp_input_status();
+#if defined(EROS_QN)
+ int devicever;
+# if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+# else
+ devicever = device_data.lcd_version;
+# endif
+ if (devicever >= 4) {
+ return axp2101_power_input_status();
+ } else
+#endif
+ {
+ int input_status = axp_input_status();
- if(input_status & AXP_INPUT_AC)
- state |= POWER_INPUT_MAIN_CHARGER;
+ if(input_status & AXP_INPUT_AC)
+ state |= POWER_INPUT_MAIN_CHARGER;
- if(input_status & AXP_INPUT_USB)
- state |= POWER_INPUT_USB_CHARGER;
+ if(input_status & AXP_INPUT_USB)
+ state |= POWER_INPUT_USB_CHARGER;
#ifdef HAVE_BATTERY_SWITCH
- if(input_status & AXP_INPUT_BATTERY)
- state |= POWER_INPUT_BATTERY;
+ if(input_status & AXP_INPUT_BATTERY)
+ state |= POWER_INPUT_BATTERY;
#endif
+ }
return state;
}
diff --git a/firmware/export/axp-2101.h b/firmware/export/axp-2101.h
new file mode 100644
index 0000000000..228e0ae2a4
--- /dev/null
+++ b/firmware/export/axp-2101.h
@@ -0,0 +1,211 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __AXP_2101_H__
+#define __AXP_2101_H__
+
+#include "config.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+/* ADC channels */
+#define AXP2101_ADC_VBAT_VOLTAGE 0
+#define AXP2101_ADC_VBUS_VOLTAGE 1
+#define AXP2101_ADC_VSYS_VOLTAGE 2
+#define AXP2101_ADC_TS_VOLTAGE 3
+#define AXP2101_ADC_DIE_TEMPERATURE 4
+#define AXP2101_NUM_ADC_CHANNELS 5
+
+/* ADC sampling rates */
+#define AXP2101_ADC_RATE_25HZ 0
+#define AXP2101_ADC_RATE_50HZ 1
+#define AXP2101_ADC_RATE_100HZ 2
+#define AXP2101_ADC_RATE_200HZ 3
+
+/* Return values of axp_battery_status() */
+#define AXP2101_BATT_DISCHARGING 0
+#define AXP2101_BATT_CHARGING 1
+#define AXP2101_BATT_FULL 2
+
+/* Bits returned by axp_input_status() */
+#define AXP2101_INPUT_AC (1 << 0)
+#define AXP2101_INPUT_USB (1 << 1)
+#define AXP2101_INPUT_BATTERY (1 << 2)
+#define AXP2101_INPUT_EXTERNAL (AXP_INPUT_AC|AXP_INPUT_USB)
+
+#define AXP2101_SUPPLY_DCDC1 0
+#define AXP2101_SUPPLY_DCDC2 1
+#define AXP2101_SUPPLY_DCDC3 2
+#define AXP2101_SUPPLY_DCDC4 3
+#define AXP2101_SUPPLY_DCDC5 4
+#define AXP2101_SUPPLY_ALDO1 5
+#define AXP2101_SUPPLY_ALDO2 6
+#define AXP2101_SUPPLY_ALDO3 7
+#define AXP2101_SUPPLY_ALDO4 8
+#define AXP2101_SUPPLY_BLDO1 9
+#define AXP2101_SUPPLY_BLDO2 10
+#define AXP2101_SUPPLY_DLDO1 11
+#define AXP2101_SUPPLY_DLDO2 12
+#define AXP2101_SUPPLY_VCPUS 13
+#define AXP2101_SUPPLY_RTCLDO1 14
+#define AXP2101_SUPPLY_RTCLDO2 15
+#define AXP2101_NUM_SUPPLIES 16
+
+/* Special values returned by axp_supply_get_voltage */
+#define AXP2101_SUPPLY_NOT_PRESENT INT_MIN
+#define AXP2101_SUPPLY_DISABLED (-1)
+
+/* AXP2101 registers */
+#define AXP2101_REG_PMU_STATUS1 0x00
+#define AXP2101_REG_PMU_STATUS2 0x01
+#define AXP2101_REG_DATA_BUFFER0 0x04
+#define AXP2101_REG_DATA_BUFFER1 0x05
+#define AXP2101_REG_DATA_BUFFER2 0x06
+#define AXP2101_REG_DATA_BUFFER3 0x07
+#define AXP2101_REG_PMUCOMMCONFIG 0x10
+#define AXP2101_REG_BATFET_CTRL 0x12
+#define AXP2101_REG_DIETEMPCTRL 0x13
+#define AXP2101_REG_MINSYSVOLTCTRL 0x14
+#define AXP2101_REG_INVOLTLIMITCTRL 0x15
+#define AXP2101_REG_INCURRLIMITCTRL 0x16
+#define AXP2101_REG_FUELGAUGERESET 0x17
+#define AXP2101_REG_PERIPHERALCTRL 0x18
+#define AXP2101_REG_WATCHDOGCTRL 0x19
+#define AXP2101_REG_LOWBATTWARN 0x1a
+#define AXP2101_REG_PWRONSTATUS 0x20
+#define AXP2101_REG_PWROFFSTATUS 0x21
+#define AXP2101_REG_PWROFFENABLE 0x22
+#define AXP2101_REG_PWROFF_DCDC_OVP_UVP 0x23
+#define AXP2101_REG_VSYSPWROFFTHRESH 0x24
+#define AXP2101_REG_PWROK_SETTING 0x25
+#define AXP2101_REG_SLEEPWAKE 0x26
+#define AXP2101_REG_LOGICTHRESH 0x27
+#define AXP2101_REG_FASTPWRON0 0x28
+#define AXP2101_REG_FASTPWRON1 0x29
+#define AXP2101_REG_FASTPWRON2 0x2a
+#define AXP2101_REG_FASTPWRON_CTRL 0x2b
+#define AXP2101_REG_ADCCHNENABLE 0x30
+#define AXP2101_REG_ADC_VBAT_H 0x34
+#define AXP2101_REG_ADC_VBAT_L 0x35
+#define AXP2101_REG_ADC_TS_H 0x36
+#define AXP2101_REG_ADC_TS_L 0x37
+#define AXP2101_REG_ADC_VBUS_H 0x38
+#define AXP2101_REG_ADC_VBUS_L 0x39
+#define AXP2101_REG_ADC_VSYS_H 0x3a
+#define AXP2101_REG_ADC_VSYS_L 0x3b
+#define AXP2101_REG_ADC_TDIE_H 0x3c
+#define AXP2101_REG_ADC_TDIE_L 0x3d
+#define AXP2101_REG_IRQEN0 0x40
+#define AXP2101_REG_IRQEN1 0x41
+#define AXP2101_REG_IRQEN2 0x42
+#define AXP2101_REG_IRQSTATUS0 0x48
+#define AXP2101_REG_IRQSTATUS1 0x49
+#define AXP2101_REG_IRQSTATUS2 0x4a
+#define AXP2101_REG_TSPINCTRL 0x50
+#define AXP2101_REG_TS_HYSL2H 0x52
+#define AXP2101_REG_TS_HYSH2L 0x53
+#define AXP2101_REG_VLTF_CHG 0x54
+#define AXP2101_REG_VHTF_CHG 0x55
+#define AXP2101_REG_VLTF_WORK 0x56
+#define AXP2101_REG_VHTF_WORK 0x57
+#define AXP2101_REG_JIETA_ENABLE 0x58
+#define AXP2101_REG_JIETA_SETTING0 0x59
+#define AXP2101_REG_JIETA_SETTING1 0x5a
+#define AXP2101_REG_JIETA_SETTING2 0x5b
+#define AXP2101_REG_IPRECHG_SETTING 0x61
+#define AXP2101_REG_ICC_SETTING 0x62
+#define AXP2101_REG_ITERM_SETTING 0x63
+#define AXP2101_REG_CV_SETTING 0x64
+#define AXP2101_REG_THERMREGTHRESH 0x65
+#define AXP2101_REG_CHARGETIMEOUT 0x67
+#define AXP2101_REG_BATTDETECTCTRL 0x68
+#define AXP2101_REG_CHGLED 0x69
+#define AXP2101_REG_BUTTONBAT 0x6a
+#define AXP2101_REG_DCDC_ONOFF 0x80
+#define AXP2101_REG_DCDC_FORCEPWM 0x81
+#define AXP2101_REG_DCDC0_VOLTAGE 0x82
+#define AXP2101_REG_DCDC1_VOLTAGE 0x83
+#define AXP2101_REG_DCDC2_VOLTAGE 0x84
+#define AXP2101_REG_DCDC3_VOLTAGE 0x85
+#define AXP2101_REG_DCDC4_VOLTAGE 0x86
+#define AXP2101_REG_LDO_ONOFF0 0x90
+#define AXP2101_REG_LDO_ONOFF1 0x91
+#define AXP2101_REG_LDO0_VOLTAGE 0x92
+#define AXP2101_REG_LDO1_VOLTAGE 0x93
+#define AXP2101_REG_LDO2_VOLTAGE 0x94
+#define AXP2101_REG_LDO3_VOLTAGE 0x94
+#define AXP2101_REG_LDO4_VOLTAGE 0x95
+#define AXP2101_REG_LDO5_VOLTAGE 0x96
+#define AXP2101_REG_LDO6_VOLTAGE 0x97
+#define AXP2101_REG_LDO7_VOLTAGE 0x98
+#define AXP2101_REG_LDO8_VOLTAGE 0x99
+#define AXP2101_REG_LDO9_VOLTAGE 0x9a
+#define AXP2101_REG_BATT_PARAMETER 0xa1
+#define AXP2101_REG_FUEL_GAUGE_CTRL 0xa2
+#define AXP2101_REG_BATT_PERCENTAGE 0xa4
+
+/* Must be called from power_init() to initialize the driver state */
+extern void axp2101_init(void);
+
+/* - axp_supply_set_voltage(): set a supply voltage to the given value
+ * in millivolts. Pass a voltage of AXP_SUPPLY_DISABLED to shut off
+ * the supply. Any invalid supply or voltage will make the call a no-op.
+ *
+ * - axp_supply_get_voltage() returns a supply voltage in millivolts.
+ * If the supply is powered off, returns AXP_SUPPLY_DISABLED.
+ * If the chip does not have the supply, returns AXP_SUPPLY_NOT_PRESENT.
+ */
+extern void axp2101_supply_set_voltage(int supply, int voltage);
+extern int axp2101_supply_get_voltage(int supply);
+
+/* Basic battery and power supply status */
+extern int axp2101_battery_status(void);
+extern int axp2101_input_status(void);
+
+/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
+ * The output of axp_adc_read() is normalized to appropriate units:
+ *
+ * - for voltages, the scale is millivolts
+ * - for currents, the scale is milliamps
+ * - for temperatures, the scale is tenths of a degree Celsius
+ * - for power, the scale is microwatts
+ *
+ * See the comment in axp_adc_conv_raw() for raw value precision/scale.
+ */
+extern int axp2101_adc_read(int adc);
+extern int axp2101_adc_read_raw(int adc);
+extern int axp2101_adc_conv_raw(int adc, int value);
+extern void axp2101_adc_set_enabled(int adc_bits);
+extern int axp2101_adc_get_rate(void);
+
+/* Set/get maximum charging current in milliamps */
+extern void axp2101_set_charge_current(int current_mA);
+extern int axp2101_get_charge_current(void);
+
+/* Set the shutdown bit */
+extern void axp2101_power_off(void);
+
+/* Debug menu */
+extern bool axp2101_debug_menu(void);
+
+extern unsigned int axp2101_power_input_status(void);
+
+#endif /* __AXP_2101_H__ */
diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h
index adb1b29e01..344f3ab726 100644
--- a/firmware/export/config/erosqnative.h
+++ b/firmware/export/config/erosqnative.h
@@ -100,6 +100,7 @@
#define HAVE_SW_POWEROFF
#ifndef SIMULATOR
+#define HAVE_AXP2101_ADDON
#define HAVE_AXP_PMU 192
#define HAVE_POWEROFF_WHILE_CHARGING
#endif
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
index 707dc372a8..c5abd7c93f 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
@@ -31,6 +31,7 @@
#include "eros_qn_codec.h"
#include <string.h>
#include <stdbool.h>
+#include "devicedata.h"
#ifndef BOOTLOADER
# include "lcd.h"
@@ -75,13 +76,16 @@ volatile signed int enc_position = 0;
/* Value of headphone detect register */
static uint8_t hp_detect_reg = 0x00;
static uint8_t hp_detect_reg_old = 0x00;
+#ifndef BOOTLOADER
static uint8_t hp_detect_debounce1 = 0x00;
+#endif
static uint8_t hp_detect_debounce2 = 0x00;
static uint8_t debounce_count = 0;
/* Interval to poll the register */
#define HPD_POLL_TIME (HZ/4)
+#ifndef BOOTLOADER
static int hp_detect_tmo_cb(struct timeout* tmo)
{
if (hp_detect_debounce1 == hp_detect_debounce2){
@@ -100,41 +104,54 @@ static int hp_detect_tmo_cb(struct timeout* tmo)
return HPD_POLL_TIME;
}
-static void hp_detect_init(void)
+static void hp_detect_init(int version)
{
- static struct timeout tmo;
- static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
- static i2c_descriptor desc = {
- .slave_addr = AXP_PMU_ADDR,
- .bus_cond = I2C_START | I2C_STOP,
- .tran_mode = I2C_READ,
- .buffer[0] = (void*)&gpio_reg,
- .count[0] = 1,
- .buffer[1] = &hp_detect_debounce1,
- .count[1] = 1,
- .callback = NULL,
- .arg = 0,
- .next = NULL,
- };
-
- /* Headphone and LO detects are wired to AXP192 GPIOs 0 and 1,
- * set them to inputs. */
- i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO0FUNCTION, 0x01); /* HP detect */
- i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* LO detect */
-
- /* Get an initial reading before startup */
- int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
- if(r >= 0)
- {
- hp_detect_reg = r;
- hp_detect_debounce1 = r;
- hp_detect_debounce2 = r;
+ if (version <= 3) {
+ static struct timeout tmo;
+ static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
+ static i2c_descriptor desc = {
+ .slave_addr = AXP_PMU_ADDR,
+ .bus_cond = I2C_START | I2C_STOP,
+ .tran_mode = I2C_READ,
+ .buffer[0] = (void*)&gpio_reg,
+ .count[0] = 1,
+ .buffer[1] = &hp_detect_debounce1,
+ .count[1] = 1,
+ .callback = NULL,
+ .arg = 0,
+ .next = NULL,
+ };
+
+ /* Headphone and LO detects are wired to AXP192 GPIOs 0 and 1,
+ * set them to inputs. */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO0FUNCTION, 0x01); /* HP detect */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* LO detect */
+
+ /* Get an initial reading before startup */
+ int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
+ if(r >= 0)
+ {
+ hp_detect_reg = r;
+ hp_detect_debounce1 = r;
+ hp_detect_debounce2 = r;
+ hp_detect_reg_old = hp_detect_reg;
+ }
+
+ /* Poll the register every second */
+ timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
+ } else {
+ uint32_t b = REG_GPIO_PIN(GPIO_B);
+
+ // initialize headphone detect variables
+ // HP_detect PB14 --> bit 4
+ // LO_detect PB22 --> bit 5
+ hp_detect_reg = ( (b>>10)&(0x10) | (b>>17)&(0x20) );
+ hp_detect_debounce1 = hp_detect_reg;
+ hp_detect_debounce2 = hp_detect_reg;
hp_detect_reg_old = hp_detect_reg;
}
-
- /* Poll the register every second */
- timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
}
+#endif
bool headphones_inserted(void)
{
@@ -187,7 +204,9 @@ void button_init_device(void)
gpio_enable_irq(GPIO_BTN_SCROLL_B);
/* Set up headphone and line out detect polling */
- hp_detect_init();
+#ifndef BOOTLOADER
+ hp_detect_init(device_data.lcd_version);
+#endif
}
/* wheel Quadrature line A interrupt */
@@ -238,16 +257,49 @@ int button_read_device(void)
if((a & (1 << 16)) == 0) r |= BUTTON_PLAY;
if((a & (1 << 17)) == 0) r |= BUTTON_VOL_UP;
if((a & (1 << 19)) == 0) r |= BUTTON_VOL_DOWN;
-
- if((b & (1 << 7)) == 0) r |= BUTTON_POWER;
- if((b & (1 << 28)) == 0) r |= BUTTON_MENU;
+#ifdef BOOTLOADER
+# if EROSQN_VER >= 4
+ if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
+ if((a & (1 << 18)) == 0) r |= BUTTON_BACK;
+# else
+ if((b & (1 << 7)) == 0) r |= BUTTON_POWER;
+ if((d & (1 << 5)) == 0) r |= BUTTON_BACK;
+# endif
+#else
+ if (device_data.lcd_version >= 4){
+ if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
+ if((a & (1 << 18)) == 0) r |= BUTTON_BACK;
+ } else {
+ if((b & (1 << 7)) == 0) r |= BUTTON_POWER;
+ if((d & (1 << 5)) == 0) r |= BUTTON_BACK;
+ }
+#endif
if((b & (1 << 28)) == 0) r |= BUTTON_MENU;
-
+
if((d & (1 << 4)) == 0) r |= BUTTON_PREV;
-
- if((d & (1 << 5)) == 0) r |= BUTTON_BACK;
if((c & (1 << 24)) == 0) r |= BUTTON_NEXT;
+#ifndef BOOTLOADER
+ if (device_data.lcd_version >= 4){
+ // get new HP/LO detect states
+ // HP_detect PB14 --> hp_detect bit 4
+ // LO_detect PB22 --> hp_detect bit 5
+ hp_detect_debounce1 = ( (b>>10)&(0x10) | (b>>17)&(0x20) );
+
+ // enter them into the debounce process
+ if (hp_detect_debounce1 == hp_detect_debounce2){
+ if (debounce_count >= 2){
+ debounce_count = 2;
+ } else {
+ debounce_count = debounce_count + 1;
+ }
+ } else {
+ debounce_count = 0;
+ hp_detect_debounce2 = hp_detect_debounce1;
+ }
+ }
+#endif
+
/* check encoder - from testing, each indent is 2 state changes or so */
if (enc_position > 1)
{
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
index 72052c261f..53b17c2098 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
@@ -1,21 +1,21 @@
/* -------------------- NOTES ------------------- */
-/* I don't think we have any devices on I2C1, the pins /may/ be reused. */
-/* DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0)) */
-
-/* OF has SD Card power listed as 0x2a - PB10, but it seems to work without. */
-
-/* I think BT power reg is pin 0x53 - C19 */
-
-/* USB_DETECT D3 chosen by trial-and-error. */
-
/* I have a suspicion this isn't right for AXP_IRQ,
* and it's not used right now anyway. copied from m3k. */
/* DEFINE_GPIO(AXP_IRQ, GPIO_PB(10), GPIOF_INPUT) */
/* ---------------------------------------------- */
+/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+/* ======================================================= */
+/* DO NOT CHANGE THE ORDER OF THESE DEFINES */
+/* WITHOUT CONSIDERING GPIO-X1000.C!! */
+/* ======================================================= */
+/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+/**************************/
+/* PIN GROUPINGS */
+/************************ */
/* Name Port Pins Function */
DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1))
DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1))
@@ -26,11 +26,24 @@ DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
/* Name Pin Function */
+/*************************/
+/* AUDIO STUFF */
+/*************************/
/* mute DAC: 0 - mute, 1 - play */
/* Note: This seems to actually be power to the DAC in general,
* at least on the ES9018K2M devices. Was "DAC_XMIT". */
DEFINE_GPIO(DAC_PWR, GPIO_PB(12), GPIOF_OUTPUT(0))
+/* Headphone Amp power */
+DEFINE_GPIO(HPAMP_POWER, GPIO_PB(6), GPIOF_OUTPUT(0))
+
+/* DAC AVDD */
+DEFINE_GPIO(DAC_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0))
+
+/* SPDIF power? */
+// gpio 53 --> port B, pin 21
+DEFINE_GPIO(SPDIF_PWR, GPIO_PB(21), GPIOF_OUTPUT(0))
+
/* mute HP amp: 0 - mute, 1 - play */
DEFINE_GPIO(HPAMP_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0))
@@ -43,33 +56,123 @@ DEFINE_GPIO(STEREOSW_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1))
*/
DEFINE_GPIO(STEREOSW_SEL, GPIO_PB(5), GPIOF_OUTPUT(0))
-/* DAC AVDD */
-DEFINE_GPIO(DAC_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0))
-
-/* Headphone Amp power */
-DEFINE_GPIO(HPAMP_POWER, GPIO_PB(6), GPIOF_OUTPUT(0))
-
+/**************/
+/* SD and USB */
+/**************/
/* SD card */
+DEFINE_GPIO(MMC_PWR, GPIO_PB(10), GPIOF_OUTPUT(1))
DEFINE_GPIO(MSC0_CD, GPIO_PB(11), GPIOF_INPUT)
/* USB */
+/* USB_DETECT D3 chosen by trial-and-error. */
DEFINE_GPIO(USB_DETECT, GPIO_PD(3), GPIOF_INPUT)
DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
+/* TCS1421_CFG (USB) CFG1 */
+// set TCS1421_CFG0:1 to 00, Upstream Facing Port (tried others, they don't work)
+// GPIO 62 --> PORT B, PIN 30
+DEFINE_GPIO(TCS1421_CFG1, GPIO_PB(30), GPIOF_OUTPUT(0))
+
+/* BL POWER */
+// gpio 89 --> port c, pin 25
+// oh, this is probably backlight power, isn't it?
+DEFINE_GPIO(BL_PWR, GPIO_PC(25), GPIOF_OUTPUT(0))
+
+/************/
+/* LCD */
+/************/
/* LCD */
-DEFINE_GPIO(LCD_PWR, GPIO_PB(14), GPIOF_OUTPUT(0))
DEFINE_GPIO(LCD_RESET, GPIO_PB(13), GPIOF_OUTPUT(0))
DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1))
DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1))
+/************/
+/* BUTTONS */
+/************/
/* Buttons */
DEFINE_GPIO(BTN_PLAY, GPIO_PA(16), GPIOF_INPUT)
DEFINE_GPIO(BTN_VOL_UP, GPIO_PA(17), GPIOF_INPUT)
DEFINE_GPIO(BTN_VOL_DOWN, GPIO_PA(19), GPIOF_INPUT)
-DEFINE_GPIO(BTN_POWER, GPIO_PB(7), GPIOF_INPUT)
DEFINE_GPIO(BTN_MENU, GPIO_PB(28), GPIOF_INPUT)
-DEFINE_GPIO(BTN_BACK, GPIO_PD(5), GPIOF_INPUT)
DEFINE_GPIO(BTN_PREV, GPIO_PD(4), GPIOF_INPUT)
DEFINE_GPIO(BTN_NEXT, GPIO_PC(24), GPIOF_INPUT)
DEFINE_GPIO(BTN_SCROLL_A, GPIO_PB(24), GPIOF_INPUT)
DEFINE_GPIO(BTN_SCROLL_B, GPIO_PB(23), GPIOF_INPUT)
+
+/**********************/
+/* BLUETOOTH STUFF */
+/**********************/
+/* BT Power */
+// GPIO 86 --> Port C, Pin 22
+DEFINE_GPIO(BT_PWR, GPIO_PC(22), GPIOF_OUTPUT(0))
+
+
+
+
+/****************************** */
+/* HW1/2/3-ONLY STUFF */
+/****************************** */
+DEFINE_GPIO(HW1_START, GPIO_PC(0), GPIOF_OUTPUT(0))
+/************/
+/* BUTTONS */
+/************/
+DEFINE_GPIO(BTN_BACK_HW1, GPIO_PD(5), GPIOF_INPUT)
+DEFINE_GPIO(BTN_POWER_HW1, GPIO_PB(7), GPIOF_INPUT)
+
+/**********************/
+/* BLUETOOTH STUFF */
+/**********************/
+// GPIO 83 --> port C, pin 19
+DEFINE_GPIO(BT_REG_ON_HW1, GPIO_PC(19), GPIOF_OUTPUT(0))
+
+/************/
+/* LCD */
+/************/
+/* LCD */
+DEFINE_GPIO(LCD_PWR_HW1, GPIO_PB(14), GPIOF_OUTPUT(0))
+DEFINE_GPIO(HW1_END, GPIO_PC(0), GPIOF_OUTPUT(0))
+
+
+
+
+/****************************** */
+/* HW4-ONLY STUFF */
+/****************************** */
+DEFINE_GPIO(HW4_START, GPIO_PC(0), GPIOF_OUTPUT(0))
+DEFINE_GPIO(BLUELIGHT_HW4, GPIO_PB(7), GPIOF_OUTPUT(1))
+/************/
+/* BUTTONS */
+/************/
+DEFINE_GPIO(BTN_BACK_HW4, GPIO_PA(18), GPIOF_INPUT)
+DEFINE_GPIO(BTN_POWER_HW4, GPIO_PB(31), GPIOF_INPUT)
+
+/**********************/
+/* BLUETOOTH STUFF */
+/**********************/
+/* bt_wake_host */
+// gpio 84 --> port C, pin 20
+DEFINE_GPIO(BT_WAKE_HOST, GPIO_PC(20), GPIOF_INPUT)
+
+/* host_wake_bt */
+// gpio 83 --> port C, pin 19
+DEFINE_GPIO(HOST_WAKE_BT, GPIO_PC(19), GPIOF_OUTPUT(0))
+
+/**************/
+/* USB */
+/**************/
+/* USB_TCS1421_CFG0 */
+// set TCS1421_CFG0:1 to 00, Upstream Facing Port (tried others, they don't work)
+// gpio 101 --> port D, pin 5
+DEFINE_GPIO(TCS1421_CFG0, GPIO_PD(5), GPIOF_OUTPUT(0))
+
+/***************/
+/* PLUG DETECT */
+/***************/
+/* HP_DETECT */
+// gpio 46 --> port B, pin 14
+DEFINE_GPIO(HP_DETECT_HW4, GPIO_PB(14), GPIOF_INPUT)
+
+/* LO_DETECT */
+// gpio 54 --> Port B, pin 22
+DEFINE_GPIO(LO_DETECT_HW4, GPIO_PB(22), GPIOF_INPUT)
+DEFINE_GPIO(HW4_END, GPIO_PC(0), GPIOF_OUTPUT(0)) \ No newline at end of file
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
index bcc30a71bd..fb39051c7a 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
@@ -292,7 +292,16 @@ void lcd_tgt_enable(bool enable)
{
if(enable) {
/* power up the panel */
- gpio_set_level(GPIO_LCD_PWR, 1);
+#ifdef BOOTLOADER
+# if EROSQN_VER <= 3
+ gpio_set_level(GPIO_LCD_PWR_HW1, 1);
+# endif
+#else
+ if (device_data.lcd_version <= 3)
+ {
+ gpio_set_level(GPIO_LCD_PWR_HW1, 1);
+ }
+#endif
mdelay(20);
gpio_set_level(GPIO_LCD_RESET, 1);
mdelay(12);
@@ -307,13 +316,13 @@ void lcd_tgt_enable(bool enable)
gpio_set_level(GPIO_LCD_CE, 0);
#ifdef BOOTLOADER
-# if EROSQN_VER == 3
+# if EROSQN_VER >= 3
lcd_exec_commands(&erosqnative_lcd_cmd_enable_v3[0]);
# else
lcd_exec_commands(&erosqnative_lcd_cmd_enable_v1[0]);
# endif
#else
- if (device_data.lcd_version == 3)
+ if (device_data.lcd_version >= 3)
{
lcd_exec_commands(&erosqnative_lcd_cmd_enable_v3[0]);
}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
index ab6393a9fe..85fddc6044 100644
--- a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
@@ -29,7 +29,9 @@
# include "usb_core.h"
#endif
#include "axp-pmu.h"
+#include "axp-2101.h"
#include "i2c-x1000.h"
+#include "devicedata.h"
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
{
@@ -58,30 +60,94 @@ void power_init(void)
{
/* Initialize driver */
i2c_x1000_set_freq(2, I2C_FREQ_400K);
- axp_init();
-
- /* Set lowest sample rate */
- axp_adc_set_rate(AXP_ADC_RATE_25HZ);
-
- /* Enable required ADCs */
- axp_adc_set_enabled(
- (1 << ADC_BATTERY_VOLTAGE) |
- (1 << ADC_CHARGE_CURRENT) |
- (1 << ADC_DISCHARGE_CURRENT) |
- (1 << ADC_VBUS_VOLTAGE) |
- (1 << ADC_VBUS_CURRENT) |
- (1 << ADC_INTERNAL_TEMP) |
- (1 << ADC_APS_VOLTAGE));
-
- /* Turn on all power outputs */
- i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
- AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
- i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
- AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
-
- /* Set the default charging current. This is the same as the
- * OF's setting, although it's not strictly within the USB spec. */
- axp_set_charge_current(780);
+
+ int devicever;
+#if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+#else
+ devicever = device_data.lcd_version;
+#endif
+ if (devicever >= 4){
+ uint8_t regread;
+ axp2101_init();
+ /* Enable required ADCs */
+ axp2101_adc_set_enabled(
+ (1 << AXP2101_ADC_VBAT_VOLTAGE) |
+ (1 << AXP2101_ADC_VBUS_VOLTAGE) |
+ (1 << AXP2101_ADC_VSYS_VOLTAGE) |
+ (1 << AXP2101_ADC_VBUS_VOLTAGE) |
+ (1 << AXP2101_ADC_TS_VOLTAGE) |
+ (1 << AXP2101_ADC_DIE_TEMPERATURE));
+
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_DCDC_ONOFF, 0, 0x1f, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_LDO_ONOFF0, 0, 0xff, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_LDO_ONOFF1, 0, 0x01, NULL);
+
+ // set power button delay to 1s to match earlier devices
+ regread = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_LOGICTHRESH);
+ if ((regread&0x03) != 0x02){
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_LOGICTHRESH, 0x03, 0, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP2101_REG_LOGICTHRESH, 0, 0x02, NULL);
+ }
+
+ // These match the OF as far as I can discern
+ // TODO: These values are set in EFUSE apparently, could
+ // do a "check then set if necessary"...
+ // Also if we had a fresh device we could verify what
+ // the OF sets.
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DCDC1, 3300);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DCDC2, 1200);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DCDC3, 2800);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DCDC4, 1800);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DCDC5, 1400);
+
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_ALDO1, 2500);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_ALDO2, 3300);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_ALDO3, 3300);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_ALDO4, 3300);
+
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_BLDO1, 3300);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_BLDO2, 3300);
+
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DLDO1, 2500);
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_DLDO2, 1250);
+
+ axp2101_supply_set_voltage(AXP2101_SUPPLY_VCPUS, 500);
+
+ /* Set the default charging current. This is the same as the
+ * OF's setting, although it's not strictly within the USB spec. */
+ axp2101_set_charge_current(780);
+ } else {
+ axp_init();
+ /* Set lowest sample rate */
+ axp_adc_set_rate(AXP_ADC_RATE_25HZ);
+
+ /* Enable required ADCs */
+ axp_adc_set_enabled(
+ (1 << ADC_BATTERY_VOLTAGE) |
+ (1 << ADC_CHARGE_CURRENT) |
+ (1 << ADC_DISCHARGE_CURRENT) |
+ (1 << ADC_VBUS_VOLTAGE) |
+ (1 << ADC_VBUS_CURRENT) |
+ (1 << ADC_INTERNAL_TEMP) |
+ (1 << ADC_APS_VOLTAGE));
+
+ /* TODO: Set Output Voltages! */
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
+
+ /* Set the default charging current. This is the same as the
+ * OF's setting, although it's not strictly within the USB spec. */
+ axp_set_charge_current(780);
+ }
#ifdef BOOTLOADER
/* Delay to give power outputs time to stabilize.
@@ -95,7 +161,18 @@ void power_init(void)
#ifdef HAVE_USB_CHARGING_ENABLE
void usb_charging_maxcurrent_change(int maxcurrent)
{
- axp_set_charge_current(maxcurrent);
+ int devicever;
+#if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+#else
+ devicever = device_data.lcd_version;
+#endif
+ if (devicever >= 4){
+ axp2101_set_charge_current(maxcurrent);
+ } else {
+ axp_set_charge_current(maxcurrent);
+ }
+
}
#endif
@@ -105,26 +182,64 @@ void adc_init(void)
void power_off(void)
{
- axp_power_off();
+ int devicever;
+#if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+#else
+ devicever = device_data.lcd_version;
+#endif
+ if (devicever >= 4){
+ axp2101_power_off();
+ } else {
+ axp_power_off();
+ }
while(1);
}
bool charging_state(void)
{
- return axp_battery_status() == AXP_BATT_CHARGING;
+ int devicever;
+#if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+#else
+ devicever = device_data.lcd_version;
+#endif
+ if (devicever >= 4){
+ return axp2101_battery_status() == AXP2101_BATT_CHARGING;
+ } else {
+ return axp_battery_status() == AXP_BATT_CHARGING;
+ }
}
int _battery_voltage(void)
{
- return axp_adc_read(ADC_BATTERY_VOLTAGE);
+ int devicever;
+#if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+#else
+ devicever = device_data.lcd_version;
+#endif
+ if (devicever >= 4){
+ return axp2101_adc_read(AXP2101_ADC_VBAT_VOLTAGE);
+ } else {
+ return axp_adc_read(ADC_BATTERY_VOLTAGE);
+ }
}
#if CONFIG_BATTERY_MEASURE & CURRENT_MEASURE
int _battery_current(void)
{
- if(charging_state())
- return axp_adc_read(ADC_CHARGE_CURRENT);
- else
- return axp_adc_read(ADC_DISCHARGE_CURRENT);
+ int devicever;
+#if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+#else
+ devicever = device_data.lcd_version;
+#endif
+ if (devicever <= 3){
+ if(charging_state())
+ return axp_adc_read(ADC_CHARGE_CURRENT);
+ else
+ return axp_adc_read(ADC_DISCHARGE_CURRENT);
+ }
}
#endif
diff --git a/firmware/target/mips/ingenic_x1000/gpio-x1000.c b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
index 0ebe424566..f826971869 100644
--- a/firmware/target/mips/ingenic_x1000/gpio-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/gpio-x1000.c
@@ -20,6 +20,9 @@
****************************************************************************/
#include "gpio-x1000.h"
+#if defined(EROS_QN)
+# include "devicedata.h"
+#endif
static const struct gpio_setting gpio_settings[PIN_COUNT] INITDATA_ATTR = {
#define DEFINE_GPIO(_name, _gpio, _func) \
@@ -59,6 +62,14 @@ static const char* const pingroup_names[PINGROUP_COUNT] = {
void gpio_init(void)
{
+#if defined(EROS_QN)
+ int devicever;
+# if defined(BOOTLOADER)
+ devicever = EROSQN_VER;
+# else
+ devicever = device_data.lcd_version;
+# endif
+#endif
/* Apply all initial GPIO settings */
for(int i = 0; i < PINGROUP_COUNT; ++i) {
const struct pingroup_setting* d = &pingroup_settings[i];
@@ -68,8 +79,29 @@ void gpio_init(void)
for(int i = 0; i < PIN_COUNT; ++i) {
const struct gpio_setting* d = &gpio_settings[i];
+
+#ifdef EROS_QN
+// eros_qn only
+ // There surely has to be a nicer way to do this...
+ // Note: PIN_ indicates position in the list,
+ // GPIO_ indicates actual port/pin number
+ // (so if you want to go based on order of the list, use the PIN_ designator!)
+ if((d->gpio != GPIO_NONE) &&\
+ ((i < PIN_HW1_START) ||\
+ (devicever <= 3 &&\
+ (i > PIN_HW1_START && i < PIN_HW1_END))\
+ ||\
+ (devicever >= 4 &&\
+ (i > PIN_HW4_START && i < PIN_HW4_END))))
+
+ // if((d->gpio != GPIO_NONE) && (d->gpio != GPIO_BTN_POWER_HW1))
+#else
+ // non-eros_qn devices
if(d->gpio != GPIO_NONE)
+#endif
+ {
gpioz_configure(GPION_PORT(d->gpio), GPION_MASK(d->gpio), d->func);
+ }
}
/* Any GPIO pins left in an IRQ trigger state need to be switched off,
diff --git a/manual/getting_started/hibyos_nativeinstall.tex b/manual/getting_started/hibyos_nativeinstall.tex
index a5c4f07fed..1a09a95074 100644
--- a/manual/getting_started/hibyos_nativeinstall.tex
+++ b/manual/getting_started/hibyos_nativeinstall.tex
@@ -40,8 +40,13 @@ may not be the most up to date, please see the wiki page at
\item Hifiwalker H2 V1.7 - V1.8
\item Surfans F20 V3.0 - V3.3
\end{itemize}
-
These players use \fname{erosqnative-hw3-erosq.upt} as the update file.
+\item[hw4 players]
+ \begin{itemize}
+ \item Hifiwalker H2 V1.9
+ \item Surfans F20 V3.4
+ \end{itemize}
+ These players use \fname{erosqnative-hw4-erosq_2024.upt} as the update file.
\end{description}
Download the \fname{.upt} file for these players from \download{bootloader/aigo/native/}.
diff --git a/manual/rockbox_interface/main.tex b/manual/rockbox_interface/main.tex
index 82192829fd..533aaa1dbf 100644
--- a/manual/rockbox_interface/main.tex
+++ b/manual/rockbox_interface/main.tex
@@ -38,6 +38,21 @@
Throughout this manual, the buttons on the \dap{} are labelled according to the
picture above.
+
+\opt{erosqnative}{
+ \note{The \dap{} has two outputs: Headphone output on the left and Line output on the right.
+ Due to the circuitry which does detection, it is recommended to only use headphones plugged
+ into the Headphone output and devices which take Line input into the Line output. If devices
+ which take a Line input are plugged into the Headphone output, it is likely that the \dap{}
+ will not detect the plug and no sound will result.
+
+ In the other direction, headphones plugged into the Line output may damage the headphones (and
+ your ears!) due to the extremely loud volume.
+
+ Note that the volume of the Line output is set by the \setting{Volume Limit} if it needs to
+ be reduced.}
+}
+
\opt{touchscreen}{
The areas of the touchscreen in the 3$\times$3 grid mode are in turn referred as follows:
\begin{table}
diff --git a/tools/configure b/tools/configure
index e4ed05884a..f7ffb77d6d 100755
--- a/tools/configure
+++ b/tools/configure
@@ -1678,9 +1678,11 @@ cat <<EOF
==FiiO== ==xDuoo== ==AIGO==
244) M3K Linux 241) X3 245) Eros Q / K
246) M3K baremetal 242) X3II 247) Eros Q / K native
- 243) X20 248) Eros Q / K native v3
- ==Shanling== (GC9A01 LCD Controller)
- 260) Q1
+ 243) X20 (hw1/hw2 bl, all hw rb)
+ ==Shanling== 248) Eros Q / K native
+ 260) Q1 (hw3 bl only)
+ 249) Eros Q / K native
+ (hw4 bl only)
EOF
buildfor=`input`;
@@ -4136,6 +4138,33 @@ fi
extradefines="$extradefines -DEROSQN_VER=3"
;;
+ 249|erosqnative_v4)
+ target_id=118
+ modelname="erosqnative_v4"
+ target="EROS_QN"
+ memory=32
+ mipsr2elcc
+ appextra="recorder:gui"
+ plugins="yes"
+ tool="$rootdir/tools/scramble -add=erosqnative "
+ boottool="$rootdir/tools/mkspl-x1000 -type=nand -ppb=2 -bpp=2 "
+ output="rockbox.erosq"
+ bootoutput="bootloader.erosq"
+ sysfontbl="16-Terminus"
+ # toolset is the tools within the tools directory that we build for
+ # this particular target.
+ toolset="$x1000tools"
+ bmp2rb_mono="$rootdir/tools/bmp2rb -f 0"
+ bmp2rb_native="$rootdir/tools/bmp2rb -f 4"
+ # architecture, manufacturer and model for the target-tree build
+ t_cpu="mips"
+ t_manufacturer="ingenic_x1000"
+ t_model="erosqnative"
+ # player version, for bootloader usage
+ # version 4
+ extradefines="$extradefines -DEROSQN_VER=4"
+ ;;
+
250|ihifi770c)
target_id=107
modelname="ihifi770c"
diff --git a/utils/rbutilqt/rbutil.ini b/utils/rbutilqt/rbutil.ini
index 339878a4b5..ba33b113ec 100644
--- a/utils/rbutilqt/rbutil.ini
+++ b/utils/rbutilqt/rbutil.ini
@@ -80,13 +80,15 @@ platform136=aigoerosq.hifiwalkerh2
platform137=aigoerosq.hifiwalkerh2.v13
platform138=aigoerosq.surfansf20
; default erosqnative should be most recent hardware revision
-platform139=erosqnative.hw3
-platform140=erosqnative.hw3.hifiwalkerh2
-platform141=erosqnative.hw3.surfansf20
-platform142=erosqnative.hw1hw2
-platform143=erosqnative.hw1hw2.hifiwalkerh2
-platform144=erosqnative.hw1hw2.hifiwalkerh2.v13
-platform145=erosqnative.hw1hw2.surfansf20
+platform139=erosqnative.hw4.hifiwalkerh2
+platform140=erosqnative.hw4.surfansf20
+platform141=erosqnative.hw3
+platform142=erosqnative.hw3.hifiwalkerh2
+platform143=erosqnative.hw3.surfansf20
+platform144=erosqnative.hw1hw2
+platform145=erosqnative.hw1hw2.hifiwalkerh2
+platform146=erosqnative.hw1hw2.hifiwalkerh2.v13
+platform147=erosqnative.hw1hw2.surfansf20
; devices sections
;
@@ -874,6 +876,32 @@ playerpic=aigoerosq
encoder=rbspeex
status=disabled
+[erosqnative.hw4.hifiwalkerh2]
+name="HIFI WALKER H2 V1.9"
+bootloadermethod=file
+bootloadername=/aigo/native/erosqnative-hw4-erosq_2024.upt
+bootloaderfile=/update.upt
+manualname=erosqnative
+themename=aigoerosq
+brand=HIFI WALKER
+usbid=0xc5020023 ; shared across EROS Q / K series
+usberror=
+playerpic=aigoerosq
+encoder=rbspeex
+
+[erosqnative.hw4.surfansf20]
+name="Surfans F20 V3.4"
+bootloadermethod=file
+bootloadername=/aigo/native/erosqnative-hw4-erosq_2024.upt
+bootloaderfile=/update.upt
+manualname=erosqnative
+themename=aigoerosq
+brand=Surfans
+usbid=0xc5020023 ; shared across EROS Q / K series
+usberror=
+playerpic=aigoerosq
+encoder=rbspeex
+
[erosqnative.hw3]
name="AIGO Eros Q V2.1"
bootloadermethod=file