summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-23 17:30:58 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-13 22:01:33 +0100
commit4c60bc9e681865fcfc149775a1ed7ccd2613d5bf (patch)
tree99f8d91af2c171cf3843f0c14d41a20d9dc29c4f /firmware/target/mips/ingenic_x1000
parent3abb7c5dd5be2ec6744bfc0a80967b20f1b59e30 (diff)
downloadrockbox-4c60bc9e681865fcfc149775a1ed7ccd2613d5bf.tar.gz
rockbox-4c60bc9e681865fcfc149775a1ed7ccd2613d5bf.zip
New port: Shanling Q1 native
- Audio playback works - Touchscreen and buttons work - Bootloader works and is capable of dual boot - Plugins are working - Cabbiev2 theme has been ported - Stable for general usage Thanks to Marc Aarts for porting Cabbiev2 and plugin bitmaps. There's a few minor known issues: - Bootloader must be installed manually using 'usbboot' as there is no support in jztool yet. - Keymaps may be lacking, need further testing and feedback. - Some plugins may not be fully adapted to the screen size and could benefit from further tweaking. - LCD shows abnormal effects under some circumstances: for example, after viewing a mostly black screen an afterimage appears briefly when going back to a brightly-lit screen. Sudden power-off without proper shutdown of the backlight causes a "dissolving" effect. - CW2015 battery reporting driver is buggy, and disabled for now. Battery reporting is currently voltage-based using the AXP192. Change-Id: I635e83f02a880192c5a82cb0861ad3a61c137c3a
Diffstat (limited to 'firmware/target/mips/ingenic_x1000')
-rw-r--r--firmware/target/mips/ingenic_x1000/debug-x1000.c6
-rw-r--r--firmware/target/mips/ingenic_x1000/msc-x1000.c13
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c2
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h0
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c191
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c63
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h33
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/boot.make31
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c195
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h56
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h32
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h40
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c399
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c140
-rw-r--r--firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c116
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c2
16 files changed, 1316 insertions, 3 deletions
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c
index fe469b1a72..1965b0b74e 100644
--- a/firmware/target/mips/ingenic_x1000/debug-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c
@@ -152,6 +152,9 @@ extern bool dbg_fiiom3k_touchpad(void);
152#ifdef HAVE_AXP_PMU 152#ifdef HAVE_AXP_PMU
153extern bool axp_debug_menu(void); 153extern bool axp_debug_menu(void);
154#endif 154#endif
155#ifdef HAVE_CW2015
156extern bool cw2015_debug_menu(void);
157#endif
155 158
156/* Menu definition */ 159/* Menu definition */
157static const struct { 160static const struct {
@@ -170,6 +173,9 @@ static const struct {
170#ifdef HAVE_AXP_PMU 173#ifdef HAVE_AXP_PMU
171 {"Power stats", &axp_debug_menu}, 174 {"Power stats", &axp_debug_menu},
172#endif 175#endif
176#ifdef HAVE_CW2015
177 {"CW2015 debug", &cw2015_debug_menu},
178#endif
173}; 179};
174 180
175static int hw_info_menu_action_cb(int btn, struct gui_synclist* lists) 181static int hw_info_menu_action_cb(int btn, struct gui_synclist* lists)
diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c
index 3b7df1dd01..d0359a53e2 100644
--- a/firmware/target/mips/ingenic_x1000/msc-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c
@@ -42,7 +42,7 @@
42#define DEBOUNCE_TIME (HZ/10) 42#define DEBOUNCE_TIME (HZ/10)
43 43
44static const msc_config msc_configs[] = { 44static const msc_config msc_configs[] = {
45#ifdef FIIO_M3K 45#if defined(FIIO_M3K)
46#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A 46#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A
47 { 47 {
48 .msc_nr = 0, 48 .msc_nr = 0,
@@ -52,6 +52,17 @@ static const msc_config msc_configs[] = {
52 .cd_gpio = GPIO_MSC0_CD, 52 .cd_gpio = GPIO_MSC0_CD,
53 .cd_active_level = 0, 53 .cd_active_level = 0,
54 }, 54 },
55#elif defined(SHANLING_Q1)
56#define MSC_CLOCK_SOURCE X1000_CLK_MPLL
57 {
58 .msc_nr = 0,
59 .msc_type = MSC_TYPE_SD,
60 .bus_width = 4,
61 .label = "microSD",
62 .cd_gpio = GPIO_MSC0_CD,
63 .cd_active_level = 0,
64 },
65 /* NOTE: SDIO wifi card is on msc1 */
55#else 66#else
56# error "Please add X1000 MSC config" 67# error "Please add X1000 MSC config"
57#endif 68#endif
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index b76efe65e5..de6eb2fb67 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -58,7 +58,7 @@
58#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4) 58#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
59 59
60const nand_chip supported_nand_chips[] = { 60const nand_chip supported_nand_chips[] = {
61#if defined(FIIO_M3K) 61#if defined(FIIO_M3K) || defined(SHANLING_Q1)
62 { 62 {
63 /* ATO25D1GA */ 63 /* ATO25D1GA */
64 .mf_id = 0x9b, 64 .mf_id = 0x9b,
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/adc-target.h
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
new file mode 100644
index 0000000000..7314f20412
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
@@ -0,0 +1,191 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "audiohw.h"
23#include "system.h"
24#include "pcm_sampr.h"
25#include "aic-x1000.h"
26#include "i2c-x1000.h"
27#include "gpio-x1000.h"
28#include "x1000/aic.h"
29#include "x1000/cpm.h"
30
31/* Codec has an dedicated oscillator connected, so it can operate
32 * as i2s master or slave. I can't distinguish any difference in
33 * terms of audio quality or power consumption. Code is left here
34 * for reference in case it proves useful to change it. */
35#define CODEC_MASTER_MODE 0
36
37static int cur_fsel = HW_FREQ_48;
38static int cur_vol_l = 0, cur_vol_r = 0;
39static int cur_filter = 0;
40static enum es9218_amp_mode cur_amp_mode = ES9218_AMP_MODE_1VRMS;
41
42static void codec_start(void)
43{
44 es9218_open();
45 es9218_mute(true);
46 es9218_set_iface_role(CODEC_MASTER_MODE ? ES9218_IFACE_ROLE_MASTER
47 : ES9218_IFACE_ROLE_SLAVE);
48 es9218_set_iface_format(ES9218_IFACE_FORMAT_I2S, ES9218_IFACE_BITS_32);
49 es9218_set_dpll_bandwidth(10);
50 es9218_set_thd_compensation(true);
51 es9218_set_thd_coeffs(0, 0);
52 audiohw_set_filter_roll_off(cur_filter);
53 audiohw_set_frequency(cur_fsel);
54 audiohw_set_volume(cur_vol_l, cur_vol_r);
55 es9218_set_amp_mode(cur_amp_mode);
56}
57
58static void codec_stop(void)
59{
60 es9218_mute(true);
61 es9218_close();
62 mdelay(1);
63}
64
65void audiohw_init(void)
66{
67 /* Configure AIC */
68 aic_set_external_codec(true);
69 aic_set_i2s_mode(CODEC_MASTER_MODE ? AIC_I2S_SLAVE_MODE
70 : AIC_I2S_MASTER_MODE);
71 aic_enable_i2s_bit_clock(true);
72
73 /* Open DAC driver */
74 i2c_x1000_set_freq(1, I2C_FREQ_400K);
75 codec_start();
76}
77
78void audiohw_postinit(void)
79{
80 es9218_mute(false);
81}
82
83void audiohw_close(void)
84{
85 codec_stop();
86}
87
88void audiohw_set_frequency(int fsel)
89{
90 int sampr = hw_freq_sampr[fsel];
91
92 /* choose clock gear setting, in line with the OF */
93 enum es9218_clock_gear clkgear;
94 if(sampr <= 48000)
95 clkgear = ES9218_CLK_GEAR_4;
96 else if(sampr <= 96000)
97 clkgear = ES9218_CLK_GEAR_2;
98 else
99 clkgear = ES9218_CLK_GEAR_1;
100
101 aic_enable_i2s_bit_clock(false);
102 es9218_set_clock_gear(clkgear);
103
104 if(CODEC_MASTER_MODE)
105 es9218_set_nco_frequency(sampr);
106 else
107 aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, 64);
108
109 aic_enable_i2s_bit_clock(true);
110
111 /* save frequency selection */
112 cur_fsel = fsel;
113}
114
115static int round_step_up(int x, int step)
116{
117 int rem = x % step;
118 if(rem > 0)
119 rem -= step;
120 return x - rem;
121}
122
123void audiohw_set_volume(int vol_l, int vol_r)
124{
125 /* save volume */
126 cur_vol_l = vol_l;
127 cur_vol_r = vol_r;
128
129 /* adjust the amp setting first */
130 int amp = round_step_up(MAX(vol_l, vol_r), ES9218_AMP_VOLUME_STEP);
131 amp = MIN(amp, ES9218_AMP_VOLUME_MAX);
132 amp = MAX(amp, ES9218_AMP_VOLUME_MIN);
133
134 /* adjust digital volumes */
135 vol_l -= amp;
136 vol_l = MIN(vol_l, ES9218_DIG_VOLUME_MAX);
137 vol_l = MAX(vol_l, ES9218_DIG_VOLUME_MIN);
138
139 vol_r -= amp;
140 vol_r = MIN(vol_r, ES9218_DIG_VOLUME_MAX);
141 vol_r = MAX(vol_r, ES9218_DIG_VOLUME_MIN);
142
143 /* program DAC */
144 es9218_set_amp_volume(amp);
145 es9218_set_dig_volume(vol_l, vol_r);
146}
147
148void audiohw_set_filter_roll_off(int value)
149{
150 cur_filter = value;
151 es9218_set_filter(value);
152}
153
154void audiohw_set_power_mode(int mode)
155{
156 enum es9218_amp_mode new_amp_mode;
157 if(mode == 0)
158 new_amp_mode = ES9218_AMP_MODE_2VRMS;
159 else
160 new_amp_mode = ES9218_AMP_MODE_1VRMS;
161
162 if(new_amp_mode != cur_amp_mode) {
163 codec_stop();
164 cur_amp_mode = new_amp_mode;
165 codec_start();
166 es9218_mute(false);
167 }
168}
169
170void es9218_set_power_pin(int level)
171{
172 gpio_set_level(GPIO_ES9218_POWER, level ? 1 : 0);
173}
174
175void es9218_set_reset_pin(int level)
176{
177 gpio_set_level(GPIO_ES9218_RESET, level ? 1 : 0);
178}
179
180uint32_t es9218_get_mclk(void)
181{
182 /* Measured by running the DAC in asynchronous I2S slave mode,
183 * and reading back the DPLL number from regs 0x42-0x45 while
184 * playing back 44.1 KHz audio.
185 *
186 * CLK = (44_100 * 2**32) / 0x4b46e5
187 * = 38_393_403.29532737
188 * ~ 38.4 Mhz
189 */
190 return 38400000;
191}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
new file mode 100644
index 0000000000..32c1b902aa
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
@@ -0,0 +1,63 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "backlight.h"
23#include "backlight-target.h"
24#include "lcd.h"
25#include "pwm-x1000.h"
26
27#define BL_LCD_CHN 0
28#define BL_LCD_PERIOD 10000
29
30static int backlight_calc_duty(int period, int min_duty, int brightness)
31{
32 return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING;
33}
34
35bool backlight_hw_init(void)
36{
37 pwm_init(BL_LCD_CHN);
38 pwm_enable(BL_LCD_CHN);
39 backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
40 return true;
41}
42
43void backlight_hw_on(void)
44{
45 pwm_enable(BL_LCD_CHN);
46#ifdef HAVE_LCD_ENABLE
47 lcd_enable(true);
48#endif
49}
50
51void backlight_hw_off(void)
52{
53 pwm_disable(BL_LCD_CHN);
54#ifdef HAVE_LCD_ENABLE
55 lcd_enable(false);
56#endif
57}
58
59void backlight_hw_brightness(int brightness)
60{
61 int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness);
62 pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns);
63}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h
new file mode 100644
index 0000000000..7298c1c06a
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/backlight-target.h
@@ -0,0 +1,33 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __BACKLIGHT_TARGET_H__
23#define __BACKLIGHT_TARGET_H__
24
25#include <stdbool.h>
26
27extern bool backlight_hw_init(void);
28
29extern void backlight_hw_on(void);
30extern void backlight_hw_off(void);
31extern void backlight_hw_brightness(int brightness);
32
33#endif /* __BACKLIGHT_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/boot.make b/firmware/target/mips/ingenic_x1000/shanlingq1/boot.make
new file mode 100644
index 0000000000..639f570ea3
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/boot.make
@@ -0,0 +1,31 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10include $(ROOTDIR)/lib/microtar/microtar.make
11
12.SECONDEXPANSION:
13
14# FIXME(q1): verify NAND parameters
15$(BUILDDIR)/spl.q1: $(BUILDDIR)/spl.bin
16 $(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@
17
18$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin
19 $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null
20
21.PHONY: $(BUILDDIR)/bootloader-info.txt
22$(BUILDDIR)/bootloader-info.txt:
23 $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@
24
25$(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.q1 \
26 $(BUILDDIR)/bootloader.ucl \
27 $(BUILDDIR)/bootloader-info.txt
28 $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \
29 --numeric-owner --no-acls --no-xattrs --no-selinux \
30 --mode=0644 --owner=0 --group=0 \
31 -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^)
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
new file mode 100644
index 0000000000..27c49a7bd7
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
@@ -0,0 +1,195 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 * Copyright (C) 2021 Dana Conrad
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23#include "button.h"
24#include "touchscreen.h"
25#include "ft6x06.h"
26#include "axp-pmu.h"
27#include "kernel.h"
28#include "backlight.h"
29#include "powermgmt.h"
30#include "gpio-x1000.h"
31#include "irq-x1000.h"
32#include "i2c-x1000.h"
33#include <stdbool.h>
34
35/* Volume wheel rotation */
36static volatile int wheel_pos = 0;
37
38/* Value of headphone detect register */
39static uint8_t hp_detect_reg = 0x00;
40
41/* Interval to poll the register */
42#define HPD_POLL_TIME (HZ/2)
43
44static int hp_detect_tmo_cb(struct timeout* tmo)
45{
46 i2c_descriptor* d = (i2c_descriptor*)tmo->data;
47 i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
48 return HPD_POLL_TIME;
49}
50
51static void hp_detect_init(void)
52{
53 /* TODO: replace this copy paste cruft with an API in axp-pmu */
54 static struct timeout tmo;
55 static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1;
56 static i2c_descriptor desc = {
57 .slave_addr = AXP_PMU_ADDR,
58 .bus_cond = I2C_START | I2C_STOP,
59 .tran_mode = I2C_READ,
60 .buffer[0] = (void*)&gpio_reg,
61 .count[0] = 1,
62 .buffer[1] = &hp_detect_reg,
63 .count[1] = 1,
64 .callback = NULL,
65 .arg = 0,
66 .next = NULL,
67 };
68
69 /* Headphone detect is wired to AXP192 GPIO: set it to input state */
70 i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01);
71
72 /* Get an initial reading before startup */
73 int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg);
74 if(r >= 0)
75 hp_detect_reg = r;
76
77 /* Poll the register every second */
78 timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
79}
80
81void button_init_device(void)
82{
83 /* Setup interrupts for the volume wheel */
84 gpio_set_function(GPIO_WHEEL1, GPIOF_IRQ_EDGE(0));
85 gpio_set_function(GPIO_WHEEL2, GPIOF_IRQ_EDGE(0));
86 gpio_flip_edge_irq(GPIO_WHEEL1);
87 gpio_flip_edge_irq(GPIO_WHEEL2);
88 gpio_enable_irq(GPIO_WHEEL1);
89 gpio_enable_irq(GPIO_WHEEL2);
90
91 /* Init touchscreen driver */
92 i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K);
93 ft6x06_init();
94
95 /* Reset touch controller */
96 gpio_set_level(GPIO_FT6x06_POWER, 1);
97 gpio_set_level(GPIO_FT6x06_RESET, 0);
98 mdelay(5);
99 gpio_set_level(GPIO_FT6x06_RESET, 1);
100
101 /* Enable ft6x06 interrupt */
102 system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT), ft6x06_irq_handler);
103 gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0));
104 gpio_enable_irq(GPIO_FT6x06_INTERRUPT);
105
106 /* Headphone detection */
107 hp_detect_init();
108}
109
110int button_read_device(int* data)
111{
112 int r = 0;
113
114 /* Read GPIO buttons, these are all active low */
115 uint32_t b = REG_GPIO_PIN(GPIO_B);
116 if((b & (1 << 21)) == 0) r |= BUTTON_PREV;
117 if((b & (1 << 22)) == 0) r |= BUTTON_NEXT;
118 if((b & (1 << 28)) == 0) r |= BUTTON_PLAY;
119 if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
120
121 /* Check the wheel */
122 int wheel_btn = 0;
123 int whpos = wheel_pos;
124 if(whpos > 3)
125 wheel_btn = BUTTON_VOL_DOWN;
126 else if(whpos < -3)
127 wheel_btn = BUTTON_VOL_UP;
128
129 if(wheel_btn) {
130 wheel_pos = 0;
131
132 /* Post the event (rapid motion is more reliable this way) */
133 queue_post(&button_queue, wheel_btn, 0);
134 queue_post(&button_queue, wheel_btn|BUTTON_REL, 0);
135
136 /* Poke the backlight */
137 backlight_on();
138 reset_poweroff_timer();
139 }
140
141 /* Handle touchscreen
142 *
143 * TODO: Support 2-point multitouch (useful for 3x3 grid mode)
144 * TODO: Support simple gestures by converting them to fake buttons
145 */
146 int t = touchscreen_to_pixels(ft6x06_state.pos_x, ft6x06_state.pos_y, data);
147 if(ft6x06_state.event == FT6x06_EVT_PRESS ||
148 ft6x06_state.event == FT6x06_EVT_CONTACT) {
149 /* Only set the button bit if the screen is being touched. */
150 r |= t;
151 }
152
153 return r;
154}
155
156void touchscreen_enable_device(bool en)
157{
158 ft6x06_enable(en);
159 /* TODO: check if it's worth shutting off the power pin */
160}
161
162bool headphones_inserted(void)
163{
164 /* TODO: Also check if the headset button is detectable via an ADC.
165 * The AXP driver should probably get proper interrupt handling,
166 * that would be useful for more things than just GPIO polling. */
167 return hp_detect_reg & 0x20 ? true : false;
168}
169
170static void handle_wheel_irq(void)
171{
172 /* Wheel stuff adapted from button-erosqnative.c */
173 static const int delta[16] = { 0, -1, 1, 0,
174 1, 0, 0, -1,
175 -1, 0, 0, 1,
176 0, 1, -1, 0 };
177 static uint32_t state = 0;
178 state <<= 2;
179 state |= (REG_GPIO_PIN(GPIO_D) >> 2) & 3;
180 state &= 0xf;
181
182 wheel_pos += delta[state];
183}
184
185void GPIOD02(void)
186{
187 handle_wheel_irq();
188 gpio_flip_edge_irq(GPIO_WHEEL1);
189}
190
191void GPIOD03(void)
192{
193 handle_wheel_irq();
194 gpio_flip_edge_irq(GPIO_WHEEL2);
195}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h
new file mode 100644
index 0000000000..905d148afa
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-target.h
@@ -0,0 +1,56 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __BUTTON_TARGET_H__
23#define __BUTTON_TARGET_H__
24
25#include <stdbool.h>
26
27/* physical buttons */
28#define BUTTON_POWER 0x00000001
29#define BUTTON_VOL_UP 0x00000002 /* up = wheel clockwise */
30#define BUTTON_VOL_DOWN 0x00000004
31#define BUTTON_PLAY 0x00000008 /* circle */
32#define BUTTON_NEXT 0x00000010 /* down */
33#define BUTTON_PREV 0x00000020 /* up */
34
35/* compatibility hacks */
36#define BUTTON_LEFT BUTTON_MIDLEFT
37#define BUTTON_RIGHT BUTTON_MIDRIGHT
38
39/* touchscreen "buttons" */
40#define BUTTON_TOPLEFT 0x00000040
41#define BUTTON_TOPMIDDLE 0x00000080
42#define BUTTON_TOPRIGHT 0x00000100
43#define BUTTON_MIDLEFT 0x00000200
44#define BUTTON_CENTER 0x00000400
45#define BUTTON_MIDRIGHT 0x00000800
46#define BUTTON_BOTTOMLEFT 0x00001000
47#define BUTTON_BOTTOMMIDDLE 0x00002000
48#define BUTTON_BOTTOMRIGHT 0x00004000
49
50#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\
51 BUTTON_PLAY|BUTTON_NEXT|BUTTON_PREV)
52
53#define POWEROFF_BUTTON BUTTON_POWER
54#define POWEROFF_COUNT 30
55
56#endif /* __BUTTON_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h
new file mode 100644
index 0000000000..7c71d12888
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/gpio-target.h
@@ -0,0 +1,32 @@
1/* Name Port Pins Function */
2DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1))
3DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1))
4DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1))
5DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1))
6DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1))
7DEFINE_PINGROUP(I2C0, GPIO_B, 3 << 23, GPIOF_DEVICE(0))
8DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
9DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
10
11/* Name Pin Function */
12DEFINE_GPIO(FT6x06_INTERRUPT, GPIO_PA(16), GPIOF_INPUT)
13DEFINE_GPIO(USB_DETECT, GPIO_PA(17), GPIOF_INPUT)
14DEFINE_GPIO(FT6x06_RESET, GPIO_PA(19), GPIOF_OUTPUT(0))
15DEFINE_GPIO(LCD_PWR, GPIO_PB(6), GPIOF_OUTPUT(1))
16DEFINE_GPIO(FT6x06_POWER, GPIO_PB(8), GPIOF_OUTPUT(0))
17DEFINE_GPIO(MSC0_CD, GPIO_PB(9), GPIOF_INPUT)
18DEFINE_GPIO(ES9218_POWER, GPIO_PB(13), GPIOF_OUTPUT(0))
19DEFINE_GPIO(LCD_RST, GPIO_PB(15), GPIOF_OUTPUT(1))
20DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1))
21DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1))
22DEFINE_GPIO(BTN_PREV, GPIO_PB(21), GPIOF_INPUT)
23DEFINE_GPIO(BTN_NEXT, GPIO_PB(22), GPIOF_INPUT)
24DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
25DEFINE_GPIO(BTN_PLAY, GPIO_PB(28), GPIOF_INPUT)
26DEFINE_GPIO(BTN_POWER, GPIO_PB(31), GPIOF_INPUT)
27DEFINE_GPIO(AXP_IRQ, GPIO_PC(21), GPIOF_INPUT)
28DEFINE_GPIO(USB_ID, GPIO_PC(23), GPIOF_INPUT)
29DEFINE_GPIO(WHEEL1, GPIO_PD(2), GPIOF_INPUT)
30DEFINE_GPIO(WHEEL2, GPIO_PD(3), GPIOF_INPUT)
31DEFINE_GPIO(ES9218_GPIO2, GPIO_PD(4), GPIOF_OUTPUT(0))
32DEFINE_GPIO(ES9218_RESET, GPIO_PD(5), GPIOF_OUTPUT(0))
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h
new file mode 100644
index 0000000000..af19aeb28c
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/i2c-target.h
@@ -0,0 +1,40 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __I2C_TARGET_H__
23#define __I2C_TARGET_H__
24
25#define I2C_ASYNC_BUS_COUNT 3
26#define I2C_ASYNC_QUEUE_SIZE 4
27
28#define FT6x06_BUS 0
29#define FT6x06_ADDR 0x38
30
31#define ES9218_BUS 1
32#define ES9218_ADDR 0x48
33
34#define AXP_PMU_BUS 2
35#define AXP_PMU_ADDR 0x34
36
37#define CW2015_BUS 2
38#define CW2015_ADDR 0x62
39
40#endif /* __I2C_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
new file mode 100644
index 0000000000..532a149185
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
@@ -0,0 +1,399 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "lcd.h"
23#include "system.h"
24#include "lcd-x1000.h"
25#include "gpio-x1000.h"
26
27/* LCD controller is probably an RM68090.
28 */
29
30static const uint32_t q1_lcd_cmd_enable[] = {
31 LCD_INSTR_CMD, 0x00,
32 LCD_INSTR_CMD, 0xbe,
33 LCD_INSTR_DAT, 0xc3,
34 LCD_INSTR_DAT, 0x29,
35
36 LCD_INSTR_CMD, 0x00,
37 LCD_INSTR_CMD, 0x01,
38 LCD_INSTR_DAT, 0x01,
39 LCD_INSTR_DAT, 0x04,
40
41 LCD_INSTR_CMD, 0x00,
42 LCD_INSTR_CMD, 0x02,
43 LCD_INSTR_DAT, 0x01,
44 LCD_INSTR_DAT, 0x00,
45
46 LCD_INSTR_CMD, 0x00,
47 LCD_INSTR_CMD, 0x03,
48 LCD_INSTR_DAT, 0x00,
49 LCD_INSTR_DAT, 0x10,
50
51 LCD_INSTR_CMD, 0x00,
52 LCD_INSTR_CMD, 0x05,
53 LCD_INSTR_DAT, 0x00,
54 LCD_INSTR_DAT, 0x00,
55
56 LCD_INSTR_CMD, 0x00,
57 LCD_INSTR_CMD, 0x06,
58 LCD_INSTR_DAT, 0x00,
59 LCD_INSTR_DAT, 0x00,
60
61 LCD_INSTR_CMD, 0x00,
62 LCD_INSTR_CMD, 0x07,
63 LCD_INSTR_DAT, 0x01,
64 LCD_INSTR_DAT, 0x03,
65
66 LCD_INSTR_CMD, 0x00,
67 LCD_INSTR_CMD, 0x08,
68 LCD_INSTR_DAT, 0x03,
69 LCD_INSTR_DAT, 0x03,
70
71 LCD_INSTR_CMD, 0x00,
72 LCD_INSTR_CMD, 0x0d,
73 LCD_INSTR_DAT, 0x00,
74 LCD_INSTR_DAT, 0x00,
75
76 LCD_INSTR_CMD, 0x00,
77 LCD_INSTR_CMD, 0x10,
78 LCD_INSTR_DAT, 0x00,
79 LCD_INSTR_DAT, 0xc1,
80
81 LCD_INSTR_CMD, 0x00,
82 LCD_INSTR_CMD, 0x11,
83 LCD_INSTR_DAT, 0xb1,
84 LCD_INSTR_DAT, 0x08,
85
86 LCD_INSTR_CMD, 0x00,
87 LCD_INSTR_CMD, 0x12,
88 LCD_INSTR_DAT, 0xb1,
89 LCD_INSTR_DAT, 0x08,
90
91 LCD_INSTR_CMD, 0x00,
92 LCD_INSTR_CMD, 0x13,
93 LCD_INSTR_DAT, 0x00,
94 LCD_INSTR_DAT, 0x0f,
95
96 LCD_INSTR_CMD, 0x00,
97 LCD_INSTR_CMD, 0x14,
98 LCD_INSTR_DAT, 0x00,
99 LCD_INSTR_DAT, 0x14,
100
101 LCD_INSTR_CMD, 0x00,
102 LCD_INSTR_CMD, 0x15,
103 LCD_INSTR_DAT, 0x00,
104 LCD_INSTR_DAT, 0x04,
105
106 LCD_INSTR_CMD, 0x00,
107 LCD_INSTR_CMD, 0x16,
108 LCD_INSTR_DAT, 0x00,
109 LCD_INSTR_DAT, 0x00,
110
111 LCD_INSTR_CMD, 0x00,
112 LCD_INSTR_CMD, 0x22,
113 LCD_INSTR_DAT, 0x00,
114 LCD_INSTR_DAT, 0x00,
115
116 LCD_INSTR_CMD, 0x00,
117 LCD_INSTR_CMD, 0x23,
118 LCD_INSTR_DAT, 0x00,
119 LCD_INSTR_DAT, 0x00,
120
121 LCD_INSTR_CMD, 0x00,
122 LCD_INSTR_CMD, 0x30,
123 LCD_INSTR_DAT, 0x7c,
124 LCD_INSTR_DAT, 0x3f,
125
126 LCD_INSTR_CMD, 0x00,
127 LCD_INSTR_CMD, 0x32,
128 LCD_INSTR_DAT, 0x00,
129 LCD_INSTR_DAT, 0x00,
130
131 LCD_INSTR_CMD, 0x00,
132 LCD_INSTR_CMD, 0x70,
133 LCD_INSTR_DAT, 0x00,
134 LCD_INSTR_DAT, 0x01,
135
136 LCD_INSTR_CMD, 0x00,
137 LCD_INSTR_CMD, 0x91,
138 LCD_INSTR_DAT, 0x01,
139 LCD_INSTR_DAT, 0x00,
140
141 LCD_INSTR_CMD, 0x00,
142 LCD_INSTR_CMD, 0xe0,
143 LCD_INSTR_DAT, 0x00,
144 LCD_INSTR_DAT, 0x01,
145
146 LCD_INSTR_CMD, 0x00,
147 LCD_INSTR_CMD, 0xe1,
148 LCD_INSTR_DAT, 0x00,
149 LCD_INSTR_DAT, 0x61,
150
151 LCD_INSTR_CMD, 0x01,
152 LCD_INSTR_CMD, 0x00,
153 LCD_INSTR_DAT, 0x10,
154 LCD_INSTR_DAT, 0x30,
155
156 LCD_INSTR_CMD, 0x01,
157 LCD_INSTR_CMD, 0x01,
158 LCD_INSTR_DAT, 0xf6,
159 LCD_INSTR_DAT, 0x3f,
160
161 LCD_INSTR_CMD, 0x01,
162 LCD_INSTR_CMD, 0x02,
163 LCD_INSTR_DAT, 0x50,
164 LCD_INSTR_DAT, 0x1f,
165
166 LCD_INSTR_CMD, 0x01,
167 LCD_INSTR_CMD, 0x03,
168 LCD_INSTR_DAT, 0x00,
169 LCD_INSTR_DAT, 0x30,
170
171 LCD_INSTR_CMD, 0x01,
172 LCD_INSTR_CMD, 0x08,
173 LCD_INSTR_DAT, 0x03,
174 LCD_INSTR_DAT, 0x00,
175
176 LCD_INSTR_CMD, 0x01,
177 LCD_INSTR_CMD, 0x11,
178 LCD_INSTR_DAT, 0x00,
179 LCD_INSTR_DAT, 0x01,
180
181 LCD_INSTR_CMD, 0x01,
182 LCD_INSTR_CMD, 0x35,
183 LCD_INSTR_DAT, 0x76,
184 LCD_INSTR_DAT, 0x66,
185
186 LCD_INSTR_CMD, 0x01,
187 LCD_INSTR_CMD, 0x39,
188 LCD_INSTR_DAT, 0x00,
189 LCD_INSTR_DAT, 0x26,
190
191 LCD_INSTR_CMD, 0x04,
192 LCD_INSTR_CMD, 0x00,
193 LCD_INSTR_DAT, 0x00,
194 LCD_INSTR_DAT, 0xc7,
195
196 LCD_INSTR_CMD, 0x04,
197 LCD_INSTR_CMD, 0x01,
198 LCD_INSTR_DAT, 0x00,
199 LCD_INSTR_DAT, 0x00,
200
201 LCD_INSTR_CMD, 0x06,
202 LCD_INSTR_CMD, 0x06,
203 LCD_INSTR_DAT, 0x00,
204 LCD_INSTR_DAT, 0x00,
205
206 LCD_INSTR_CMD, 0x03,
207 LCD_INSTR_CMD, 0x00,
208 LCD_INSTR_DAT, 0x0d,
209 LCD_INSTR_DAT, 0x0e,
210
211 LCD_INSTR_CMD, 0x03,
212 LCD_INSTR_CMD, 0x01,
213 LCD_INSTR_DAT, 0x00,
214 LCD_INSTR_DAT, 0x03,
215
216 LCD_INSTR_CMD, 0x03,
217 LCD_INSTR_CMD, 0x02,
218 LCD_INSTR_DAT, 0x08,
219 LCD_INSTR_DAT, 0x08,
220
221 LCD_INSTR_CMD, 0x03,
222 LCD_INSTR_CMD, 0x03,
223 LCD_INSTR_DAT, 0x02,
224 LCD_INSTR_DAT, 0x01,
225
226 LCD_INSTR_CMD, 0x03,
227 LCD_INSTR_CMD, 0x04,
228 LCD_INSTR_DAT, 0x03,
229 LCD_INSTR_DAT, 0x01,
230
231 LCD_INSTR_CMD, 0x03,
232 LCD_INSTR_CMD, 0x05,
233 LCD_INSTR_DAT, 0x00,
234 LCD_INSTR_DAT, 0x04,
235
236 LCD_INSTR_CMD, 0x03,
237 LCD_INSTR_CMD, 0x06,
238 LCD_INSTR_DAT, 0x1b,
239 LCD_INSTR_DAT, 0x21,
240
241 LCD_INSTR_CMD, 0x03,
242 LCD_INSTR_CMD, 0x07,
243 LCD_INSTR_DAT, 0x0f,
244 LCD_INSTR_DAT, 0x0e,
245
246 LCD_INSTR_CMD, 0x03,
247 LCD_INSTR_CMD, 0x08,
248 LCD_INSTR_DAT, 0x01,
249 LCD_INSTR_DAT, 0x04,
250
251 LCD_INSTR_CMD, 0x03,
252 LCD_INSTR_CMD, 0x09,
253 LCD_INSTR_DAT, 0x08,
254 LCD_INSTR_DAT, 0x08,
255
256 LCD_INSTR_CMD, 0x03,
257 LCD_INSTR_CMD, 0x0a,
258 LCD_INSTR_DAT, 0x02,
259 LCD_INSTR_DAT, 0x01,
260
261 LCD_INSTR_CMD, 0x03,
262 LCD_INSTR_CMD, 0x0b,
263 LCD_INSTR_DAT, 0x03,
264 LCD_INSTR_DAT, 0x01,
265
266 LCD_INSTR_CMD, 0x03,
267 LCD_INSTR_CMD, 0x0c,
268 LCD_INSTR_DAT, 0x00,
269 LCD_INSTR_DAT, 0x03,
270
271 LCD_INSTR_CMD, 0x03,
272 LCD_INSTR_CMD, 0x0d,
273 LCD_INSTR_DAT, 0x31,
274 LCD_INSTR_DAT, 0x34,
275
276 /* X start */
277 LCD_INSTR_CMD, 0x02,
278 LCD_INSTR_CMD, 0x10,
279 LCD_INSTR_DAT, 0x00,
280 LCD_INSTR_DAT, 0x1e, /* 30 */
281
282 /* X end */
283 LCD_INSTR_CMD, 0x02,
284 LCD_INSTR_CMD, 0x11,
285 LCD_INSTR_DAT, 0x01,
286 LCD_INSTR_DAT, 0x85, /* 389 */
287
288 /* Y start */
289 LCD_INSTR_CMD, 0x02,
290 LCD_INSTR_CMD, 0x12,
291 LCD_INSTR_DAT, 0x00,
292 LCD_INSTR_DAT, 0x00, /* 0 */
293
294 /* Y end */
295 LCD_INSTR_CMD, 0x02,
296 LCD_INSTR_CMD, 0x13,
297 LCD_INSTR_DAT, 0x01,
298 LCD_INSTR_DAT, 0x8f, /* 399 */
299
300 /* RAM write start X? */
301 LCD_INSTR_CMD, 0x02,
302 LCD_INSTR_CMD, 0x00,
303 LCD_INSTR_DAT, 0x00,
304 LCD_INSTR_DAT, 0x1e,
305
306 /* RAM write start Y? */
307 LCD_INSTR_CMD, 0x02,
308 LCD_INSTR_CMD, 0x01,
309 LCD_INSTR_DAT, 0x00,
310 LCD_INSTR_DAT, 0x00,
311
312 LCD_INSTR_CMD, 0x00,
313 LCD_INSTR_CMD, 0x03,
314 LCD_INSTR_DAT, 0x00,
315 LCD_INSTR_DAT, 0x30,
316
317 LCD_INSTR_CMD, 0x02,
318 LCD_INSTR_CMD, 0x02,
319 LCD_INSTR_END,
320};
321
322/* NOTE this sleep mode may not be saving power, but it gets rid of the
323 * ghost image that would otherwise remain on the display */
324static const uint32_t q1_lcd_cmd_sleep[] = {
325 LCD_INSTR_CMD, 0x00,
326 LCD_INSTR_CMD, 0x10,
327 LCD_INSTR_DAT, 0x00,
328 LCD_INSTR_DAT, 0x03,
329
330 LCD_INSTR_CMD, 0x00,
331 LCD_INSTR_CMD, 0x07,
332 LCD_INSTR_DAT, 0x01,
333 LCD_INSTR_DAT, 0x01,
334
335 LCD_INSTR_END,
336};
337
338static const uint32_t q1_lcd_cmd_wake[] = {
339 LCD_INSTR_CMD, 0x00,
340 LCD_INSTR_CMD, 0x07,
341 LCD_INSTR_DAT, 0x01,
342 LCD_INSTR_DAT, 0x03,
343
344 LCD_INSTR_CMD, 0x00,
345 LCD_INSTR_CMD, 0x10,
346 LCD_INSTR_DAT, 0x00,
347 LCD_INSTR_DAT, 0xc1,
348
349 LCD_INSTR_END,
350};
351
352static const uint8_t __attribute__((aligned(64)))
353 q1_lcd_dma_wr_cmd[] = {0x02, 0x02, 0x02, 0x02};
354
355const struct lcd_tgt_config lcd_tgt_config = {
356 .bus_width = 8,
357 .cmd_width = 8,
358 .use_6800_mode = 0,
359 .use_serial = 0,
360 .clk_polarity = 0,
361 .dc_polarity = 0,
362 .wr_polarity = 1,
363 .te_enable = 0,
364 .big_endian = 1,
365 .dma_wr_cmd_buf = &q1_lcd_dma_wr_cmd,
366 .dma_wr_cmd_size = sizeof(q1_lcd_dma_wr_cmd),
367};
368
369void lcd_tgt_enable(bool enable)
370{
371 if(enable) {
372 /* power on the panel */
373 gpio_set_level(GPIO_LCD_PWR, 1);
374 gpio_set_level(GPIO_LCD_RST, 1);
375 gpio_set_level(GPIO_LCD_CE, 1);
376 gpio_set_level(GPIO_LCD_RD, 1);
377 mdelay(50);
378 gpio_set_level(GPIO_LCD_RST, 0);
379 mdelay(100);
380 gpio_set_level(GPIO_LCD_RST, 1);
381 mdelay(50);
382 gpio_set_level(GPIO_LCD_CE, 0);
383
384 /* Start the controller */
385 lcd_set_clock(X1000_CLK_MPLL, 50000000);
386 lcd_exec_commands(q1_lcd_cmd_enable);
387 } else {
388 /* FIXME: Shanling Q1 LCD power down sequence
389 * not important because we don't use it but it'd be nice to know */
390 }
391}
392
393void lcd_tgt_sleep(bool sleep)
394{
395 if(sleep)
396 lcd_exec_commands(q1_lcd_cmd_sleep);
397 else
398 lcd_exec_commands(q1_lcd_cmd_wake);
399}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
new file mode 100644
index 0000000000..17fbe1cede
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
@@ -0,0 +1,140 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "power.h"
23#include "adc.h"
24#include "system.h"
25#include "axp-pmu.h"
26#ifdef HAVE_CW2015
27# include "cw2015.h"
28#endif
29#ifdef HAVE_USB_CHARGING_ENABLE
30# include "usb_core.h"
31#endif
32
33#include "i2c-x1000.h"
34
35/* TODO: Better(?) battery reporting for Q1 using CW2015 driver
36 *
37 * The CW2015 has its own quirks so the driver has to be more complicated
38 * than "read stuff from I2C," unfortunately. Without fixing the quirks it
39 * is probably worse than the simple voltage-based method.
40 *
41 * A bigger problem is that it shares an I2C bus with the AXP192, but when
42 * we attempt to communicate with both chips, they start returning bogus
43 * data intermittently. Ususally, reads will return 0 but sometimes they
44 * can return other nonzero bogus data. It could be that one or the other is
45 * pulling the bus line down inappropriately, or maybe the hardware does not
46 * respect the bus free time between start/stop conditions and one of the
47 * devices is getting confused.
48 */
49
50const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
51{
52 3470
53};
54
55/* the OF shuts down at this voltage */
56const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
57{
58 3400
59};
60
61/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
62const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
63{
64 { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 }
65};
66
67/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
68const unsigned short percent_to_volt_charge[11] =
69{
70 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
71};
72
73void power_init(void)
74{
75 i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K);
76 axp_init();
77#ifdef HAVE_CW2015
78 cw2015_init();
79#endif
80
81 /* Change supply voltage from the default of 1250 mV to 1200 mV,
82 * this matches the original firmware's settings. Didn't observe
83 * any obviously bad behavior at 1250 mV, but better to be safe. */
84 axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200);
85
86 /* For now, just turn everything on... definitely the touchscreen
87 * is powered by one of the outputs */
88 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
89 AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL);
90 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
91 AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL);
92 i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
93 AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
94
95 /* Delay to give power output time to stabilize */
96 mdelay(20);
97}
98
99#ifdef HAVE_USB_CHARGING_ENABLE
100void usb_charging_maxcurrent_change(int maxcurrent)
101{
102 axp_set_charge_current(maxcurrent);
103}
104#endif
105
106void power_off(void)
107{
108 axp_power_off();
109 while(1);
110}
111
112bool charging_state(void)
113{
114 return axp_battery_status() == AXP_BATT_CHARGING;
115}
116
117int _battery_voltage(void)
118{
119 /* CW2015 can also read battery voltage, but the AXP consistently
120 * reads ~20-30 mV higher so I suspect it's the "real" voltage. */
121 return axp_adc_read(ADC_BATTERY_VOLTAGE);
122}
123
124#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) != 0
125int _battery_level(void)
126{
127 return cw2015_get_soc();
128}
129#endif
130
131#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & TIME_MEASURE) != 0
132int _battery_time(void)
133{
134 return cw2015_get_rrt();
135}
136#endif
137
138void adc_init(void)
139{
140}
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
new file mode 100644
index 0000000000..33303c5e6b
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
@@ -0,0 +1,116 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "system.h"
23#include "clk-x1000.h"
24#include "spl-x1000.h"
25#include "gpio-x1000.h"
26
27#define CMDLINE_COMMON \
28 "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
29#define CMDLINE_NORMAL \
30 " init=/linuxrc ubi.mtd=5 root=ubi0:rootfs ubi.mtd=6 rootfstype=ubifs rw"
31
32static int dualboot_setup(void)
33{
34 spl_dualboot_init_clocktree();
35 spl_dualboot_init_uart2();
36
37 /* load PDMA MCU firmware */
38 jz_writef(CPM_CLKGR, PDMA(0));
39 return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
40}
41
42const struct spl_boot_option spl_boot_options[] = {
43 [BOOT_OPTION_ROCKBOX] = {
44 .storage_addr = 0x6800,
45 .storage_size = 102 * 1024,
46 .load_addr = X1000_DRAM_BASE,
47 .exec_addr = X1000_DRAM_BASE,
48 .flags = BOOTFLAG_UCLPACK,
49 },
50 [BOOT_OPTION_OFW_PLAYER] = {
51 .storage_addr = 0x140000,
52 .storage_size = 8 * 1024 * 1024,
53 .load_addr = 0x80efffc0,
54 .exec_addr = 0x80f00000,
55 .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
56 .cmdline_addr = 0x80004000,
57 .setup = dualboot_setup,
58 },
59 [BOOT_OPTION_OFW_RECOVERY] = {
60 .storage_addr = 0x940000,
61 .storage_size = 10 * 1024 * 1024,
62 .load_addr = 0x80efffc0,
63 .exec_addr = 0x80f00000,
64 .cmdline = CMDLINE_COMMON,
65 .cmdline_addr = 0x80004000,
66 .setup = dualboot_setup,
67 },
68};
69
70int spl_get_boot_option(void)
71{
72 /* Button debounce time in OST clock cycles */
73 const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
74
75 /* Buttons to poll */
76 const unsigned port = GPIO_B;
77 const uint32_t recov_pin = (1 << 22); /* Next */
78 const uint32_t orig_fw_pin = (1 << 21); /* Prev */
79
80 uint32_t pin = -1, lastpin = 0;
81 uint32_t deadline = 0;
82 int iter_count = 30; /* to avoid an infinite loop */
83
84 /* set GPIOs to input state */
85 gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
86
87 /* Poll until we get a stable reading */
88 do {
89 lastpin = pin;
90 pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
91 if(pin != lastpin) {
92 deadline = __ost_read32() + btn_stable_time;
93 iter_count -= 1;
94 }
95 } while(iter_count > 0 && __ost_read32() < deadline);
96
97 if(iter_count >= 0 && (pin & orig_fw_pin)) {
98 if(pin & recov_pin)
99 return BOOT_OPTION_OFW_RECOVERY;
100 else
101 return BOOT_OPTION_OFW_PLAYER;
102 }
103
104 return BOOT_OPTION_ROCKBOX;
105}
106
107void spl_error(void)
108{
109 /* Flash the backlight */
110 int level = 0;
111 while(1) {
112 gpio_set_function(GPIO_PC(25), GPIOF_OUTPUT(level));
113 mdelay(100);
114 level = 1 - level;
115 }
116}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index 284b963e97..72dc53b2b7 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -34,7 +34,7 @@
34#include "ucl_decompress.h" 34#include "ucl_decompress.h"
35#include <string.h> 35#include <string.h>
36 36
37#ifdef FIIO_M3K 37#if defined(FIIO_M3K) || defined(SHANLING_Q1)
38# define SPL_DDR_MEMORYSIZE 64 38# define SPL_DDR_MEMORYSIZE 64
39# define SPL_DDR_AUTOSR_EN 1 39# define SPL_DDR_AUTOSR_EN 1
40# define SPL_DDR_NEED_BYPASS 1 40# define SPL_DDR_NEED_BYPASS 1