summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
authorDana Conrad <dconrad@fastmail.com>2021-06-26 12:07:11 -0500
committerAidan MacDonald <amachronic@protonmail.com>2021-07-18 12:14:35 +0000
commit3e7a09cb0dee0ee04b5c77f427bf89d990ec8d0b (patch)
treebe1eb7e1d4166f60b245a0603e9f7dc11e1f5614 /firmware/target
parent64a24591aec049682167b193700a07572fc04c4c (diff)
New Port: Eros Q Native
What works: - LCD: 16-bit RGB565 - all buttons, including scrollwheel - SD Card - Battery level and charging/not charging status - USB - audio - sample rate switching - HP / LO detect, with "safe" fixed LO volume - LO volume will only be put to user-defined max volume if headphones are not present. - rtc - Plugins build, tried a couple and they seem OK - Bootloader, installable to nand via usbboot What doesn't work: - Dual Boot - power on/off has intermittent, low volume audio click (sometimes it's completely silent, sometimes there's a click) - Audio uses 16-bit volume scaling, so clicking/popping is pretty bad at lower volumes - need 32 bit volume scaling, 24 bit I2S data - USB HID keys not yet defined - no jztool support Unknowns: - Stereo Switch pins: Direction select, AC_DC (probably not even hooked up) - What is the actual purpose of the Stereo Swtich? - How does the bluetooth module connect? "Someday" stuff: - get LCD working at higher bit depth - Bluetooth Change-Id: I70dda8fc092c6e3f4352f2245e4164193f803c33
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c4
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c90
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c63
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h33
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/boot.make30
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c247
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/button-target.h45
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h71
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h31
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c188
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c110
-rw-r--r--firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c63
-rw-r--r--firmware/target/mips/ingenic_x1000/lcd-x1000.c4
-rw-r--r--firmware/target/mips/ingenic_x1000/msc-x1000.c10
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c2
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c4
17 files changed, 992 insertions, 3 deletions
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
index 7e254401fb..4988e7c3bf 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -265,7 +265,7 @@ void clk_init(void)
jz_writef(CPM_APCR, BS(1), PLLM(42 - 1), PLLN(0), PLLOD(0), ENABLE(1));
while(jz_readf(CPM_APCR, ON) == 0);
-#if defined(FIIO_M3K)
+#if (defined(FIIO_M3K) || defined(EROS_QN))
/* TODO: Allow targets to define their clock frequencies in their config,
* instead of having this be a random special case. */
if(get_boot_option() == BOOT_OPTION_ROCKBOX) {
@@ -296,7 +296,7 @@ void clk_init(void)
CLKMUX_CPU(SCLK_A) |
CLKMUX_AHB0(MPLL) |
CLKMUX_AHB2(MPLL));
-#if defined(FIIO_M3K)
+#if (defined(FIIO_M3K) || defined(EROS_QN))
}
#endif
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
new file mode 100644
index 0000000000..7bb985650c
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c
@@ -0,0 +1,90 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "audiohw.h"
+#include "system.h"
+#include "pcm_sampr.h"
+#include "aic-x1000.h"
+#include "i2c-x1000.h"
+#include "gpio-x1000.h"
+#include "logf.h"
+
+/* Audio path appears to be:
+ * DAC --> HP Amp --> Stereo Switch --> HP OUT
+ * \--> LO OUT
+ *
+ * The real purpose of the Stereo Switch is not clear.
+ * It appears to switch sources between the HP amp and something,
+ * likely something unimplemented. */
+
+void audiohw_init(void)
+{
+ /* explicitly mute everything */
+ gpio_set_level(GPIO_MAX97220_SHDN, 0);
+ gpio_set_level(GPIO_ISL54405_MUTE, 1);
+ gpio_set_level(GPIO_PCM5102A_XMIT, 0);
+
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(AIC_I2S_MASTER_MODE);
+ audiohw_set_frequency(HW_FREQ_48);
+
+ aic_enable_i2s_master_clock(true);
+ aic_enable_i2s_bit_clock(true);
+
+ mdelay(10);
+
+ /* power on DAC and HP Amp */
+ gpio_set_level(GPIO_PCM5102A_ANALOG_PWR, 1);
+ gpio_set_level(GPIO_MAX97220_POWER, 1);
+}
+
+void audiohw_postinit(void)
+{
+ /* unmute - attempt to make power-on pop-free */
+ gpio_set_level(GPIO_ISL54405_SEL, 0);
+ gpio_set_level(GPIO_MAX97220_SHDN, 1);
+ mdelay(10);
+ gpio_set_level(GPIO_PCM5102A_XMIT, 1);
+ mdelay(10);
+ gpio_set_level(GPIO_ISL54405_MUTE, 0);
+}
+
+/* TODO: get shutdown just right according to dac datasheet */
+void audiohw_close(void)
+{
+ /* mute - attempt to make power-off pop-free */
+ gpio_set_level(GPIO_ISL54405_MUTE, 1);
+ mdelay(10);
+ gpio_set_level(GPIO_PCM5102A_XMIT, 0);
+ mdelay(10);
+ gpio_set_level(GPIO_MAX97220_SHDN, 0);
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ int sampr = hw_freq_sampr[fsel];
+ int mult = 256;
+
+ aic_enable_i2s_bit_clock(false);
+ aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, mult);
+ aic_enable_i2s_bit_clock(true);
+}
+
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
new file mode 100644
index 0000000000..2c86a995db
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "backlight.h"
+#include "backlight-target.h"
+#include "lcd.h"
+#include "pwm-x1000.h"
+
+#define BL_LCD_CHN 0
+#define BL_LCD_PERIOD 30000
+
+static int backlight_calc_duty(int period, int min_duty, int brightness)
+{
+ return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING;
+}
+
+bool backlight_hw_init(void)
+{
+ pwm_init(BL_LCD_CHN);
+ pwm_enable(BL_LCD_CHN);
+ backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
+ return true;
+}
+
+void backlight_hw_on(void)
+{
+ pwm_enable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(true);
+#endif
+}
+
+void backlight_hw_off(void)
+{
+ pwm_disable(BL_LCD_CHN);
+#ifdef HAVE_LCD_ENABLE
+ lcd_enable(false);
+#endif
+}
+
+void backlight_hw_brightness(int brightness)
+{
+ int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness);
+ pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns);
+}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h
new file mode 100644
index 0000000000..05d6e36679
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 __BACKLIGHT_TARGET_H__
+#define __BACKLIGHT_TARGET_H__
+
+#include <stdbool.h>
+
+extern bool backlight_hw_init(void);
+
+extern void backlight_hw_on(void);
+extern void backlight_hw_off(void);
+extern void backlight_hw_brightness(int brightness);
+
+#endif /* __BACKLIGHT_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/boot.make b/firmware/target/mips/ingenic_x1000/erosqnative/boot.make
new file mode 100644
index 0000000000..b6d0844cd0
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/boot.make
@@ -0,0 +1,30 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+include $(ROOTDIR)/lib/microtar/microtar.make
+
+.SECONDEXPANSION:
+
+$(BUILDDIR)/spl.erosq: $(BUILDDIR)/spl.bin
+ $(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@
+
+$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin
+ $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null
+
+.PHONY: $(BUILDDIR)/bootloader-info.txt
+$(BUILDDIR)/bootloader-info.txt:
+ $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@
+
+$(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.erosq \
+ $(BUILDDIR)/bootloader.ucl \
+ $(BUILDDIR)/bootloader-info.txt
+ $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \
+ --numeric-owner --no-acls --no-xattrs --no-selinux \
+ --mode=0644 --owner=0 --group=0 \
+ -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^)
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
new file mode 100644
index 0000000000..1583db175a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c
@@ -0,0 +1,247 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "button.h"
+#include "kernel.h"
+#include "backlight.h"
+#include "powermgmt.h"
+#include "panic.h"
+#include "axp-pmu.h"
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "i2c-x1000.h"
+#include "eros_qn_codec.h"
+#include <string.h>
+#include <stdbool.h>
+
+#ifndef BOOTLOADER
+# include "lcd.h"
+# include "font.h"
+#endif
+
+/* ===========================================
+ * | OLD STATE | NEW STATE | DIRECTION |
+ * | 0 0 | 0 0 | 0: NO CHANGE |
+ * | 0 0 | 0 1 | -1: CCW |
+ * | 0 0 | 1 0 | 1: CW |
+ * | 0 0 | 1 1 | 0: INVALID |
+ * | 0 1 | 0 0 | 1: CW |
+ * | 0 1 | 0 1 | 0: NO CHANGE |
+ * | 0 1 | 1 0 | 0: INVALID |
+ * | 0 1 | 1 1 | -1: CCW |
+ * | 1 0 | 0 0 | -1: CCW |
+ * | 1 0 | 0 1 | 0: INVALID |
+ * | 1 0 | 1 0 | 0: NO CHANGE |
+ * | 1 0 | 1 1 | 1: CW |
+ * | 1 1 | 0 0 | 0: INVALID |
+ * | 1 1 | 0 1 | 1: CW |
+ * | 1 1 | 1 0 | -1: CCW |
+ * | 1 1 | 1 1 | 0: NO CHANGE |
+ * ===========================================
+ *
+ * Quadrature explanation since it's not plainly obvious how this works:
+ *
+ * If either of the quadrature lines change, we can look up the combination
+ * of previous state and new state in the table above (enc_state[] below)
+ * and it tells us whether to add 1, subtract 1, or no change from the sum (enc_position).
+ * This also gives us a nice debounce, since each state can only have 1 pin change
+ * at a time. I didn't come up with this, but I've used it before and it works well.
+ *
+ * Old state is 2 higher bits, new state is 2 lower bits of enc_current_state. */
+
+/* list of valid quadrature states and their directions */
+signed char enc_state[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
+volatile unsigned char enc_current_state = 0;
+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;
+
+/* Interval to poll the register */
+#define HPD_POLL_TIME (HZ/2)
+
+static int hp_detect_tmo_cb(struct timeout* tmo)
+{
+ i2c_descriptor* d = (i2c_descriptor*)tmo->data;
+ i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
+ return HPD_POLL_TIME;
+}
+
+static void hp_detect_init(void)
+{
+ 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_reg,
+ .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_reg_old = hp_detect_reg;
+ }
+
+ /* Poll the register every second */
+ timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
+}
+
+bool headphones_inserted(void)
+{
+ /* if the status has changed, set the output volume accordingly */
+ if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30))
+ {
+ hp_detect_reg_old = hp_detect_reg;
+#if !defined(BOOTLOADER)
+ pcm5102_set_outputs();
+#endif
+ }
+ return hp_detect_reg & 0x10 ? false : true;
+}
+
+bool lineout_inserted(void)
+{
+ /* if the status has changed, set the output volume accordingly */
+ if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30))
+ {
+ hp_detect_reg_old = hp_detect_reg;
+#if !defined(BOOTLOADER)
+ pcm5102_set_outputs();
+#endif
+ }
+ return hp_detect_reg & 0x20 ? false : true;
+}
+
+/* Rockbox interface */
+void button_init_device(void)
+{
+ /* set both quadrature lines to interrupts */
+ gpio_set_function(GPIO_BTN_SCROLL_A, GPIOF_IRQ_EDGE(1));
+ gpio_set_function(GPIO_BTN_SCROLL_B, GPIOF_IRQ_EDGE(1));
+
+ /* set interrupts to fire on the next edge based on current state */
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_A);
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_B);
+
+ /* get current state of both encoder gpios */
+ enc_current_state = (REG_GPIO_PIN(GPIO_B)>>21) & 0x0c;
+
+ /* enable quadrature interrupts */
+ gpio_enable_irq(GPIO_BTN_SCROLL_A);
+ gpio_enable_irq(GPIO_BTN_SCROLL_B);
+
+ /* Set up headphone and line out detect polling */
+ hp_detect_init();
+}
+
+/* wheel Quadrature line A interrupt */
+void GPIOB24(void)
+{
+ /* fill state with previous (2 higher bits) and current (2 lower bits) */
+ enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03);
+
+ /* look up in table */
+ enc_position = enc_position + enc_state[(enc_current_state)];
+
+ /* move current state to previous state if valid data */
+ if (enc_state[(enc_current_state)] != 0)
+ enc_current_state = (enc_current_state << 2);
+
+ /* we want the other edge next time */
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_A);
+}
+
+/* wheel Quadrature line B interrupt */
+void GPIOB23(void)
+{
+ /* fill state with previous (2 higher bits) and current (2 lower bits) */
+ enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03);
+
+ /* look up in table */
+ enc_position = enc_position + enc_state[(enc_current_state)];
+
+ /* move current state to previous state if valid data */
+ if (enc_state[(enc_current_state)] != 0)
+ enc_current_state = (enc_current_state << 2);
+
+ /* we want the other edge next time */
+ gpio_flip_edge_irq(GPIO_BTN_SCROLL_B);
+}
+
+int button_read_device(void)
+{
+ int r = 0;
+
+ /* Read GPIOs for normal buttons */
+ uint32_t a = REG_GPIO_PIN(GPIO_A);
+ uint32_t b = REG_GPIO_PIN(GPIO_B);
+ uint32_t c = REG_GPIO_PIN(GPIO_C);
+ uint32_t d = REG_GPIO_PIN(GPIO_D);
+
+ /* All buttons are active low */
+ 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;
+ 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;
+
+ /* check encoder - from testing, each indent is 2 state changes or so */
+ if (enc_position > 1)
+ {
+ /* need to use queue_post() in order to do BUTTON_SCROLL_*,
+ * Rockbox treats these buttons differently. */
+ queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
+ enc_position = 0;
+ }
+ else if (enc_position < -1)
+ {
+ /* need to use queue_post() in order to do BUTTON_SCROLL_*,
+ * Rockbox treats these buttons differently. */
+ queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
+ enc_position = 0;
+ }
+
+ return r;
+}
+
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h
new file mode 100644
index 0000000000..9c39a40296
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2021 Solomon Peachy, Dana Conrad
+ *
+ * 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 _BUTTON_TARGET_H_
+#define _BUTTON_TARGET_H_
+
+/* Main unit's buttons */
+#define BUTTON_POWER 0x00000001
+#define BUTTON_MENU 0x00000002
+#define BUTTON_BACK 0x00000004
+#define BUTTON_PLAY 0x00000008
+#define BUTTON_NEXT 0x00000010
+#define BUTTON_PREV 0x00000020
+#define BUTTON_VOL_UP 0x00000040
+#define BUTTON_VOL_DOWN 0x00000080
+#define BUTTON_SCROLL_BACK 0x00000100
+#define BUTTON_SCROLL_FWD 0x00000200
+
+#define BUTTON_MAIN (BUTTON_POWER | BUTTON_MENU | BUTTON_BACK | BUTTON_PREV | \
+ BUTTON_NEXT | BUTTON_PLAY | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD)
+
+#define BUTTON_LEFT BUTTON_PREV
+#define BUTTON_RIGHT BUTTON_NEXT
+
+/* Software power-off */
+#define POWEROFF_BUTTON BUTTON_POWER
+#define POWEROFF_COUNT 25
+
+#endif /* _BUTTON_TARGET_H_ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
new file mode 100644
index 0000000000..376eae136e
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h
@@ -0,0 +1,71 @@
+/* -------------------- 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) */
+
+/* ---------------------------------------------- */
+
+/* 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))
+DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1))
+DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
+
+/* Name Pin Function */
+/* mute DAC - 0 - mute, 1 - play. Affects both HP and LO. */
+DEFINE_GPIO(PCM5102A_XMIT, GPIO_PB(12), GPIOF_OUTPUT(0))
+
+/* mute HP amp, no effect on LO. 0 - mute, 1 - play */
+DEFINE_GPIO(MAX97220_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0))
+
+/* mute audio mux, only affects Headphone out.
+ * 0 - play, 1 - mute */
+DEFINE_GPIO(ISL54405_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1))
+
+/* switches HP on/off - 0 HP on, 1 hp off, has no effect on LO.
+ * As best I can tell, it switches HP Out sources between HP amp and something
+ * not implemented - there seem to be resistors missing. */
+DEFINE_GPIO(ISL54405_SEL, GPIO_PB(5), GPIOF_OUTPUT(0))
+
+/* DAC AVDD */
+DEFINE_GPIO(PCM5102A_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0))
+
+/* Headphone Amp power */
+DEFINE_GPIO(MAX97220_POWER, GPIO_PB(6), GPIOF_OUTPUT(0))
+
+/* SD card */
+DEFINE_GPIO(MSC0_CD, GPIO_PB(11), GPIOF_INPUT)
+
+/* USB */
+DEFINE_GPIO(USB_DETECT, GPIO_PD(3), GPIOF_INPUT)
+DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
+
+/* 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 */
+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)
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
new file mode 100644
index 0000000000..8d0b8a6e20
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 __I2C_TARGET_H__
+#define __I2C_TARGET_H__
+
+#define I2C_ASYNC_BUS_COUNT 3
+#define I2C_ASYNC_QUEUE_SIZE 4
+
+#define AXP_PMU_BUS 2
+#define AXP_PMU_ADDR 0x34
+
+#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
new file mode 100644
index 0000000000..073bddb8b4
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c
@@ -0,0 +1,188 @@
+
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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 "lcd.h"
+#include "kernel.h"
+#include "lcd-x1000.h"
+#include "gpio-x1000.h"
+#include "system.h"
+
+/* for reference on these command/data hex values, see the mipi dcs lcd spec. *
+ * Not everything here is there, but all the standard stuff is. */
+
+static const uint32_t erosqnative_lcd_cmd_enable[] = {
+ /* Set EXTC? */
+ LCD_INSTR_CMD, 0xc8,
+ LCD_INSTR_DAT, 0xff,
+ LCD_INSTR_DAT, 0x93,
+ LCD_INSTR_DAT, 0x42,
+ /* Set Address Mode */
+ LCD_INSTR_CMD, 0x36,
+ LCD_INSTR_DAT, 0xd8,
+ /* Pixel Format Set */
+ LCD_INSTR_CMD, 0x3a,
+ //LCD_INSTR_DAT, 0x66, /* OF specified 18 bpp */
+ LCD_INSTR_DAT, 0x05, /* RB seems to be happier dealing with 16 bits/pixel */
+ /* Power Control 1? */
+ LCD_INSTR_CMD, 0xc0,
+ LCD_INSTR_DAT, 0x15,
+ LCD_INSTR_DAT, 0x15,
+ /* Power Control 2? */
+ LCD_INSTR_CMD, 0xc1,
+ LCD_INSTR_DAT, 0x01,
+ /* VCOM? */
+ LCD_INSTR_CMD, 0xc5,
+ LCD_INSTR_DAT, 0xda,
+ /* ?? */
+ LCD_INSTR_CMD, 0xb1,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x1b,
+ /* ?? */
+ LCD_INSTR_CMD, 0xb4,
+ LCD_INSTR_DAT, 0x02,
+ /* Positive gamma correction? */
+ LCD_INSTR_CMD, 0xe0,
+ LCD_INSTR_DAT, 0x0f,
+ LCD_INSTR_DAT, 0x13,
+ LCD_INSTR_DAT, 0x17,
+ LCD_INSTR_DAT, 0x04,
+ LCD_INSTR_DAT, 0x13,
+ LCD_INSTR_DAT, 0x07,
+ LCD_INSTR_DAT, 0x40,
+ LCD_INSTR_DAT, 0x39,
+ LCD_INSTR_DAT, 0x4f,
+ LCD_INSTR_DAT, 0x06,
+ LCD_INSTR_DAT, 0x0d,
+ LCD_INSTR_DAT, 0x0a,
+ LCD_INSTR_DAT, 0x1f,
+ LCD_INSTR_DAT, 0x22,
+ LCD_INSTR_DAT, 0x00,
+ /* Negative gamma correction? */
+ LCD_INSTR_CMD, 0xe1,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x21,
+ LCD_INSTR_DAT, 0x24,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x0f,
+ LCD_INSTR_DAT, 0x05,
+ LCD_INSTR_DAT, 0x38,
+ LCD_INSTR_DAT, 0x32,
+ LCD_INSTR_DAT, 0x49,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x09,
+ LCD_INSTR_DAT, 0x08,
+ LCD_INSTR_DAT, 0x32,
+ LCD_INSTR_DAT, 0x35,
+ LCD_INSTR_DAT, 0x0f,
+ /* Exit Sleep */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 120000,
+ /* Display On */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_UDELAY, 20000,
+};
+
+/* sleep and wake copied directly from m3k */
+static const uint32_t erosqnative_lcd_cmd_sleep[] = {
+ /* Display OFF */
+ LCD_INSTR_CMD, 0x28,
+ /* Sleep IN */
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_UDELAY, 5000,
+ LCD_INSTR_END,
+};
+
+static const uint32_t erosqnative_lcd_cmd_wake[] = {
+ /* Sleep OUT */
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_UDELAY, 5000,
+ /* Display ON */
+ LCD_INSTR_CMD, 0x29,
+ LCD_INSTR_END,
+};
+
+/* As far as I can tell, this is a sequence of commands sent before each
+ * DMA set. Original in OF was:
+ * {0x2c, 0x2c, 0x2c, 0x2c}
+ * But this set from the m3k seems to work the same, and makes more sense
+ * to me:
+ * {0x00, 0x00, 0x00, 0x2c}
+ * This command is more than likely going to be the same
+ * for any old mipi lcd on the market, maybe. I really don't think we need
+ * to send "write_memory_start four times in a row. */
+static const uint8_t __attribute__((aligned(64)))
+ erosqnative_lcd_dma_wr_cmd[] = {0x2c, 0x2c, 0x2c, 0x2c};
+
+const struct lcd_tgt_config lcd_tgt_config = {
+ .bus_width = 8,
+ .cmd_width = 8,
+ .use_6800_mode = 0,
+ .use_serial = 0,
+ .clk_polarity = 0,
+ .dc_polarity = 0,
+ .wr_polarity = 1,
+ .te_enable = 0, /* OF had TE enabled (1) */
+ .te_polarity = 1,
+ .te_narrow = 0,
+ .dma_wr_cmd_buf = &erosqnative_lcd_dma_wr_cmd,
+ .dma_wr_cmd_size = sizeof(erosqnative_lcd_dma_wr_cmd),
+};
+
+void lcd_tgt_enable(bool enable)
+{
+ if(enable) {
+ /* power up the panel */
+ gpio_set_level(GPIO_LCD_PWR, 1);
+ mdelay(20);
+ gpio_set_level(GPIO_LCD_RESET, 1);
+ mdelay(12);
+
+ /* set the clock */
+ lcd_set_clock(X1000_CLK_SCLK_A, 20000000);
+
+ /* toggle chip select low (active) */
+ gpio_set_level(GPIO_LCD_RD, 1);
+ gpio_set_level(GPIO_LCD_CE, 1);
+ mdelay(5);
+ gpio_set_level(GPIO_LCD_CE, 0);
+
+ lcd_exec_commands(&erosqnative_lcd_cmd_enable[0]);
+ } else {
+ /* doesn't flash white if we don't do anything... */
+#if 0
+ lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]);
+
+ mdelay(115); // copied from m3k
+
+ gpio_set_level(GPIO_LCD_PWR, 0);
+ gpio_set_level(GPIO_LCD_RESET, 0);
+#endif
+ }
+}
+
+void lcd_tgt_sleep(bool sleep)
+{
+ if(sleep)
+ lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]);
+ else
+ lcd_exec_commands(&erosqnative_lcd_cmd_wake[0]);
+}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
new file mode 100644
index 0000000000..c466db66b1
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c
@@ -0,0 +1,110 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald, Dana Conrad
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+// whole file copied from m3k
+
+#include "power.h"
+#include "adc.h"
+#include "system.h"
+#include "kernel.h"
+#ifdef HAVE_USB_CHARGING_ENABLE
+# include "usb_core.h"
+#endif
+#include "axp-pmu.h"
+#include "i2c-x1000.h"
+
+const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
+{
+ 3470
+};
+
+/* the OF shuts down at this voltage */
+const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
+{
+ 3400
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
+const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
+{
+ { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 }
+};
+
+/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
+const unsigned short percent_to_volt_charge[11] =
+{
+ 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
+};
+
+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);
+
+ /* Ensure battery voltage ADC is enabled */
+ int bits = axp_adc_get_enabled();
+ bits |= (1 << ADC_BATTERY_VOLTAGE);
+ axp_adc_set_enabled(bits);
+
+ /* 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);
+
+ /* Short delay to give power outputs time to stabilize */
+ mdelay(5);
+}
+
+#ifdef HAVE_USB_CHARGING_ENABLE
+void usb_charging_maxcurrent_change(int maxcurrent)
+{
+ axp_set_charge_current(maxcurrent);
+}
+#endif
+
+void adc_init(void)
+{
+}
+
+void power_off(void)
+{
+ axp_power_off();
+ while(1);
+}
+
+bool charging_state(void)
+{
+ return axp_battery_status() == AXP_BATT_CHARGING;
+}
+
+int _battery_voltage(void)
+{
+ return axp_adc_read(ADC_BATTERY_VOLTAGE);
+}
diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c
new file mode 100644
index 0000000000..9d7a1d118a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "system.h"
+#include "clk-x1000.h"
+#include "spl-x1000.h"
+#include "gpio-x1000.h"
+
+/* TODO: get dual-boot working */
+
+const struct spl_boot_option spl_boot_options[] = {
+ [BOOT_OPTION_ROCKBOX] = {
+ .storage_addr = 0x6800,
+ .storage_size = 102 * 1024,
+ .load_addr = X1000_DRAM_BASE,
+ .exec_addr = X1000_DRAM_BASE,
+ .flags = BOOTFLAG_UCLPACK,
+ },
+};
+
+int spl_get_boot_option(void)
+{
+ return BOOT_OPTION_ROCKBOX;
+}
+
+void spl_error(void)
+{
+ const uint32_t pin = (1 << 25);
+
+ /* Turn on backlight */
+ jz_clr(GPIO_INT(GPIO_C), pin);
+ jz_set(GPIO_MSK(GPIO_C), pin);
+ jz_clr(GPIO_PAT1(GPIO_C), pin);
+ jz_set(GPIO_PAT0(GPIO_C), pin);
+
+ while(1) {
+ /* Turn it off */
+ mdelay(100);
+ jz_set(GPIO_PAT0(GPIO_C), pin);
+
+ /* Turn it on */
+ mdelay(100);
+ jz_clr(GPIO_PAT0(GPIO_C), pin);
+ }
+}
diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
index 193ff082e0..b66359a598 100644
--- a/firmware/target/mips/ingenic_x1000/lcd-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.c
@@ -457,6 +457,10 @@ void lcd_update(void)
jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1));
}
+/* We can do partial updates even though the DMA doesn't seem to handle it well,
+ * due to the fact that this is actually putting it into a buffer, and then
+ * it gets transferred via DMA to a secondary buffer, which gets transferred in
+ * its entirety to the LCD through a different DMA process. */
void lcd_update_rect(int x, int y, int width, int height)
{
/* Clamp the coordinates */
diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c
index d0359a53e2..dbc1ebbe89 100644
--- a/firmware/target/mips/ingenic_x1000/msc-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c
@@ -63,6 +63,16 @@ static const msc_config msc_configs[] = {
.cd_active_level = 0,
},
/* NOTE: SDIO wifi card is on msc1 */
+#elif defined(EROS_QN)
+#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A
+ {
+ .msc_nr = 0,
+ .msc_type = MSC_TYPE_SD,
+ .bus_width = 4,
+ .label = "microSD",
+ .cd_gpio = GPIO_MSC0_CD,
+ .cd_active_level = 0,
+ },
#else
# error "Please add X1000 MSC config"
#endif
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index de6eb2fb67..a818ba10aa 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -58,7 +58,7 @@
#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
const nand_chip supported_nand_chips[] = {
-#if defined(FIIO_M3K) || defined(SHANLING_Q1)
+#if defined(FIIO_M3K) || defined(SHANLING_Q1) || defined(EROS_QN)
{
/* ATO25D1GA */
.mf_id = 0x9b,
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index 72dc53b2b7..afaf5a7dd6 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -38,6 +38,10 @@
# define SPL_DDR_MEMORYSIZE 64
# define SPL_DDR_AUTOSR_EN 1
# define SPL_DDR_NEED_BYPASS 1
+#elif defined(EROS_QN)
+# define SPL_DDR_MEMORYSIZE 32
+# define SPL_DDR_AUTOSR_EN 1
+# define SPL_DDR_NEED_BYPASS 1
#else
# error "please define DRAM settings"
#endif