diff --git a/apps/SOURCES b/apps/SOURCES
index 5c49f0bbbc..6fdaea3ca8 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -298,4 +298,6 @@ keymaps/keymap-fiiom3klinux.c
keymaps/keymap-fiiom3k.c
#elif CONFIG_KEYPAD == EROSQ_PAD
keymaps/keymap-erosq.c
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+keymaps/keymap-shanlingq1.c
#endif
diff --git a/apps/features.txt b/apps/features.txt
index 4e7f986057..83c3f0a65f 100644
--- a/apps/features.txt
+++ b/apps/features.txt
@@ -182,12 +182,12 @@ depth_3d
#endif
/* This should be AUDIOHW_HAVE_FILTER_ROLL_OFF but that is only defined later */
-#if defined(DX50) || defined(HAVE_DF1704_CODEC) || defined(HAVE_PCM1792_CODEC) || defined(HAVE_CS4398) || defined(HAVE_WM8740) || defined(HAVE_ES9018) || defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_AK4376)
+#if defined(DX50) || defined(HAVE_DF1704_CODEC) || defined(HAVE_PCM1792_CODEC) || defined(HAVE_CS4398) || defined(HAVE_WM8740) || defined(HAVE_ES9018) || defined(HAVE_XDUOO_LINUX_CODEC) || defined(HAVE_FIIO_LINUX_CODEC) || defined(HAVE_AK4376) || defined(HAVE_ES9218)
filter_roll_off
#endif
/* This should be AUDIOHW_HAVE_POWER_MODE but that is not defined yet */
-#if defined(HAVE_AK4376)
+#if defined(HAVE_AK4376) || defined(HAVE_ES9218)
dac_power_mode
#endif
@@ -195,6 +195,10 @@ dac_power_mode
es9018
#endif
+#if defined(HAVE_ES9218)
+es9218
+#endif
+
/* These features are only used by the manual so they won't break binary
* compatibility
*/
diff --git a/apps/keymaps/keymap-shanlingq1.c b/apps/keymaps/keymap-shanlingq1.c
new file mode 100644
index 0000000000..4745139e7a
--- /dev/null
+++ b/apps/keymaps/keymap-shanlingq1.c
@@ -0,0 +1,77 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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.
+ *
+ ****************************************************************************/
+
+/* Button Code Definitions for Shanling Q1 target */
+
+#include "config.h"
+#include "action.h"
+#include "button.h"
+#include "settings.h"
+
+/* {Action Code, Button code, Prereq button code } */
+
+static const struct button_mapping button_context_standard[] = {
+ {ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE},
+ {ACTION_STD_NEXT, BUTTON_NEXT, BUTTON_NONE},
+ {ACTION_STD_OK, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
+ {ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ LAST_ITEM_IN_LIST
+}; /* button_context_standard */
+
+static const struct button_mapping button_context_wps[] = {
+ {ACTION_WPS_PLAY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
+ {ACTION_WPS_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REL, BUTTON_NONE},
+ {ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REL, BUTTON_NONE},
+ {ACTION_WPS_SKIPNEXT, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT},
+ {ACTION_WPS_SKIPPREV, BUTTON_PREV|BUTTON_REL, BUTTON_PREV},
+ {ACTION_WPS_SEEKFWD, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_STOPSEEK, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT|BUTTON_REPEAT},
+ {ACTION_WPS_SEEKBACK, BUTTON_PREV|BUTTON_REPEAT, BUTTON_NONE},
+ {ACTION_WPS_STOPSEEK, BUTTON_PREV|BUTTON_REL, BUTTON_PREV|BUTTON_REPEAT},
+ {ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
+ LAST_ITEM_IN_LIST
+}; /* button_context_wps */
+
+static const struct button_mapping button_context_list[] = {
+ {ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REL, BUTTON_NONE},
+ {ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REL, BUTTON_NONE},
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
+}; /* button_context_list */
+
+const struct button_mapping* target_get_context_mapping(int context)
+{
+ switch (context)
+ {
+ default:
+ case CONTEXT_STD:
+ return button_context_standard;
+ case CONTEXT_WPS:
+ return button_context_wps;
+ case CONTEXT_TREE:
+ case CONTEXT_CUSTOM|CONTEXT_TREE:
+ case CONTEXT_MAINMENU:
+ case CONTEXT_BOOKMARKSCREEN:
+ //return button_context_tree;
+ case CONTEXT_LIST:
+ return button_context_list;
+ }
+}
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 4cbc6f30f6..f268434f60 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12204,6 +12204,142 @@
es9018: "Bypass"
+
+ id: LANG_FILTER_LINEAR_FAST
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Linear Fast"
+
+
+ *: none
+ es9218: "Linear Fast"
+
+
+
+ id: LANG_FILTER_LINEAR_SLOW
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Linear Slow"
+
+
+ *: none
+ es9218: "Linear Slow"
+
+
+
+ id: LANG_FILTER_MINIMUM_FAST
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Minimum Fast"
+
+
+ *: none
+ es9218: "Minimum Fast"
+
+
+
+ id: LANG_FILTER_MINIMUM_SLOW
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Minimum Slow"
+
+
+ *: none
+ es9218: "Minimum Slow"
+
+
+
+ id: LANG_FILTER_APODIZING_1
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Apodizing type 1"
+
+
+ *: none
+ es9218: "Apodizing type 1"
+
+
+
+ id: LANG_FILTER_APODIZING_2
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Apodizing type 2"
+
+
+ *: none
+ es9218: "Apodizing type 2"
+
+
+
+ id: LANG_FILTER_HYBRID_FAST
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Hybrid Fast"
+
+
+ *: none
+ es9218: "Hybrid Fast"
+
+
+
+ id: LANG_FILTER_BRICK_WALL
+ desc: in sound settings
+ user: core
+
+
+ *: none
+ es9218: "Brick Wall"
+
+
+ *: none
+ es9218: "Brick Wall"
+
+
id: LANG_DAC_POWER_MODE
desc: in sound settings
@@ -12211,14 +12347,17 @@
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
*: none
dac_power_mode: "DAC's power mode"
+ es9218: "DAC's output level"
@@ -12228,14 +12367,17 @@
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
*: none
dac_power_mode: "High performance"
+ es9218: "High Gain (2 Vrms)"
@@ -12245,14 +12387,17 @@
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
*: none
dac_power_mode: "Save battery"
+ es9218: "Low Gain (1 Vrms)"
diff --git a/apps/plugins/battery_bench.c b/apps/plugins/battery_bench.c
index 2534e3bebe..6c477cbd09 100644
--- a/apps/plugins/battery_bench.c
+++ b/apps/plugins/battery_bench.c
@@ -234,6 +234,9 @@
#define BATTERY_ON_TXT "Play"
#define BATTERY_OFF_TXT "Power"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "No keymap defined!"
#endif
diff --git a/apps/plugins/bitmaps/mono/SOURCES b/apps/plugins/bitmaps/mono/SOURCES
index e6a24b2bc5..eb00bd9e8a 100644
--- a/apps/plugins/bitmaps/mono/SOURCES
+++ b/apps/plugins/bitmaps/mono/SOURCES
@@ -19,7 +19,8 @@ bubbles_bubble.138x110x1.bmp
((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
bubbles_bubble.220x176x1.bmp
#elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
- ((LCD_WIDTH == 240) && (LCD_HEIGHT >= 320))
+ ((LCD_WIDTH == 240) && (LCD_HEIGHT >= 320)) || \
+ ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400))
bubbles_bubble.320x240x1.bmp
#elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 48eb18dfa4..37d0060213 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -158,6 +158,9 @@ jackpot_slots.30x420x1.bmp
((LCD_WIDTH >= 480) && (LCD_HEIGHT >= 640))
bubbles_emblem.640x480x16.bmp
bubbles_background.640x480x16.bmp
+#elif (LCD_WIDTH >= 360) && (LCD_HEIGHT >= 400)
+bubbles_emblem.360x400x16.bmp
+bubbles_background.360x400x16.bmp
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
bubbles_emblem.320x240x16.bmp
bubbles_background.320x240x16.bmp
@@ -388,7 +391,9 @@ invadrox_shield.22x16x16.bmp
invadrox_ufo.16x7x16.bmp
invadrox_ufo_explode.21x8x16.bmp
invadrox_numbers.50x7x16.bmp
-#if LCD_WIDTH == 320
+#if LCD_WIDTH == 360 && LCD_HEIGHT == 400
+invadrox_background.360x400x16.bmp
+#elif LCD_WIDTH == 320
invadrox_background.320x240x16.bmp
#elif LCD_WIDTH == 240
invadrox_background.240x320x16.bmp
@@ -457,6 +462,8 @@ jewels.220x176x16.bmp
jewels.320x240x16.bmp
#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
jewels.320x240x16.bmp
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+jewels.360x400x16.bmp
#elif ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
jewels.640x480x16.bmp
@@ -546,6 +553,8 @@ puzzles_cursor.11x16x24.bmp
#if LCD_DEPTH >= 16 /* colour versions*/
#if (LCD_WIDTH == 640) && (LCD_HEIGHT == 480)
rockblox_background.640x480x16.bmp
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+rockblox_background.360x400x16.bmp
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
rockblox_background.320x240x16.bmp
#elif (LCD_WIDTH == 240) && (LCD_HEIGHT >= 320)
@@ -603,6 +612,12 @@ snake2_header2.640x480x16.bmp
snake2_left.640x480x16.bmp
snake2_right.640x480x16.bmp
snake2_bottom.640x480x16.bmp
+#elif (LCD_WIDTH >= 360) && (LCD_HEIGHT >= 400) && (LCD_DEPTH >= 16)
+snake2_header1.360x400x16.bmp
+snake2_header2.360x400x16.bmp
+snake2_left.360x400x16.bmp
+snake2_right.360x400x16.bmp
+snake2_bottom.360x400x16.bmp
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240) && (LCD_DEPTH >= 16)
snake2_header1.320x240x16.bmp
snake2_header2.320x240x16.bmp
@@ -874,7 +889,8 @@ superdom_boarditems.176x132x16.bmp
#elif (LCD_WIDTH == 320 && LCD_HEIGHT == 240)
superdom_boarditems.320x240x16.bmp
#elif ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || \
- ((LCD_WIDTH == 240) && (LCD_HEIGHT == 400))
+ ((LCD_WIDTH == 240) && (LCD_HEIGHT == 400)) || \
+ ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400))
superdom_boarditems.240x320x16.bmp
#elif (LCD_WIDTH == 480 && LCD_HEIGHT == 640)
superdom_boarditems.480x640x16.bmp
@@ -911,6 +927,8 @@ sliding_puzzle.132x132x16.bmp
sliding_puzzle.176x176x16.bmp
#elif SMALLER_DIMENSION <= 240
sliding_puzzle.240x240x16.bmp
+#elif SMALLER_DIMENSION <= 360
+sliding_puzzle.360x360x16.bmp
#elif SMALLER_DIMENSION <= 480
sliding_puzzle.480x480x16.bmp
#endif
diff --git a/apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmp b/apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmp
new file mode 100644
index 0000000000..46d8133911
Binary files /dev/null and b/apps/plugins/bitmaps/native/bubbles_background.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmp b/apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmp
new file mode 100644
index 0000000000..d34e466749
Binary files /dev/null and b/apps/plugins/bitmaps/native/bubbles_emblem.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmp b/apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmp
new file mode 100644
index 0000000000..14c687a940
Binary files /dev/null and b/apps/plugins/bitmaps/native/invadrox_background.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/jewels.360x400x16.bmp b/apps/plugins/bitmaps/native/jewels.360x400x16.bmp
new file mode 100644
index 0000000000..5cd35f2162
Binary files /dev/null and b/apps/plugins/bitmaps/native/jewels.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmp b/apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmp
new file mode 100644
index 0000000000..b2b1289045
Binary files /dev/null and b/apps/plugins/bitmaps/native/rockblox_background.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmp b/apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmp
new file mode 100644
index 0000000000..e74e3ecb6f
Binary files /dev/null and b/apps/plugins/bitmaps/native/sliding_puzzle.360x360x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmp
new file mode 100644
index 0000000000..f6da503ab3
Binary files /dev/null and b/apps/plugins/bitmaps/native/snake2_bottom.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmp
new file mode 100644
index 0000000000..f88acf2571
Binary files /dev/null and b/apps/plugins/bitmaps/native/snake2_header1.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmp
new file mode 100644
index 0000000000..2acccd36b4
Binary files /dev/null and b/apps/plugins/bitmaps/native/snake2_header2.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/snake2_left.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_left.360x400x16.bmp
new file mode 100644
index 0000000000..eac0c8e601
Binary files /dev/null and b/apps/plugins/bitmaps/native/snake2_left.360x400x16.bmp differ
diff --git a/apps/plugins/bitmaps/native/snake2_right.360x400x16.bmp b/apps/plugins/bitmaps/native/snake2_right.360x400x16.bmp
new file mode 100644
index 0000000000..316f40cb4c
Binary files /dev/null and b/apps/plugins/bitmaps/native/snake2_right.360x400x16.bmp differ
diff --git a/apps/plugins/blackjack.c b/apps/plugins/blackjack.c
index 4242f5a89f..24cfbe583c 100644
--- a/apps/plugins/blackjack.c
+++ b/apps/plugins/blackjack.c
@@ -607,6 +607,10 @@ enum {
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define BJACK_QUIT BUTTON_POWER
+#define BJACK_QUIT_NAME "QUIT"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/brickmania.c b/apps/plugins/brickmania.c
index 92f8d4d161..4983d5a417 100644
--- a/apps/plugins/brickmania.c
+++ b/apps/plugins/brickmania.c
@@ -350,6 +350,9 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/bubbles.c b/apps/plugins/bubbles.c
index 50de10fa23..ffc814e7a0 100644
--- a/apps/plugins/bubbles.c
+++ b/apps/plugins/bubbles.c
@@ -120,6 +120,17 @@ enum {
#define XOFS 128
#define MAX_FPS 40
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+#define XOFS 86
+#define YOFS 100
+#define SCORE_TXT_X 28
+#define SCORE_TXT_WIDTH 32
+#define SCORE_TXT_Y 48
+#define LEVEL_TXT_X 27
+#define LEVEL_TXT_WIDTH 32
+#define LEVEL_TXT_Y 5
+#define MAX_FPS 40
+
/* 22x22 bubbles (iPod Video) */
#elif (LCD_HEIGHT == 240) && (LCD_WIDTH == 320)
#define XOFS 72
diff --git a/apps/plugins/calculator.c b/apps/plugins/calculator.c
index cbaea8aa7e..bd09330529 100644
--- a/apps/plugins/calculator.c
+++ b/apps/plugins/calculator.c
@@ -533,6 +533,9 @@ F3: equal to "="
#define CALCULATOR_CALC BUTTON_MENU
#define CALCULATOR_CLEAR BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define CALCULATOR_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/calendar.c b/apps/plugins/calendar.c
index e7f221a3c9..3299a81273 100644
--- a/apps/plugins/calendar.c
+++ b/apps/plugins/calendar.c
@@ -421,6 +421,9 @@
#define CALENDAR_NEXT_MONTH BUTTON_VOL_UP
#define CALENDAR_PREV_MONTH BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "No keypad setting."
#endif
diff --git a/apps/plugins/chessbox/chessbox_pgn.h b/apps/plugins/chessbox/chessbox_pgn.h
index 827f045a4b..9d4f369ecc 100644
--- a/apps/plugins/chessbox/chessbox_pgn.h
+++ b/apps/plugins/chessbox/chessbox_pgn.h
@@ -581,6 +581,9 @@
#define CB_SCROLL_LEFT (BUTTON_LEFT|BUTTON_REPEAT)
#define CB_SCROLL_RIGHT (BUTTON_RIGHT|BUTTON_REPEAT)
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chessclock.c b/apps/plugins/chessclock.c
index 91b04e713a..3d03cb9f6a 100644
--- a/apps/plugins/chessclock.c
+++ b/apps/plugins/chessclock.c
@@ -395,6 +395,9 @@
#define CHC_SETTINGS_OK BUTTON_SELECT
#define CHC_SETTINGS_CANCEL BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define CHC_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/chip8.c b/apps/plugins/chip8.c
index 31866acd10..ac7f998690 100644
--- a/apps/plugins/chip8.c
+++ b/apps/plugins/chip8.c
@@ -1291,6 +1291,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define CHIP8_KEY6 BUTTON_RIGHT
#define CHIP8_KEY8 BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/clix.c b/apps/plugins/clix.c
index c127a300f2..9cd66a8034 100644
--- a/apps/plugins/clix.c
+++ b/apps/plugins/clix.c
@@ -316,6 +316,9 @@
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define CLIX_BUTTON_QUIT BUTTON_POWER
+
#else
#error "no keymap"
#endif
diff --git a/apps/plugins/cube.c b/apps/plugins/cube.c
index 857f2415d4..dfd7df8951 100644
--- a/apps/plugins/cube.c
+++ b/apps/plugins/cube.c
@@ -400,6 +400,9 @@
#define CUBE_PAUSE BUTTON_PLAY
#define CUBE_HIGHSPEED BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/doom/i_video.c b/apps/plugins/doom/i_video.c
index 2381a7614d..f5d07a8354 100644
--- a/apps/plugins/doom/i_video.c
+++ b/apps/plugins/doom/i_video.c
@@ -614,6 +614,10 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_WEAPON BUTTON_VOL_UP
#define DOOMBUTTON_MAP BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define DOOMBUTTON_ESC BUTTON_POWER
+#define DOOMBUTTON_MAP BUTTON_PREV
+
#else
#error Keymap not defined!
#endif
diff --git a/apps/plugins/flipit.c b/apps/plugins/flipit.c
index f6fb059f91..c659cf5ba5 100644
--- a/apps/plugins/flipit.c
+++ b/apps/plugins/flipit.c
@@ -496,6 +496,9 @@
#define FLIPIT_STEP_BY_STEP BUTTON_VOL_UP
#define FLIPIT_TOGGLE BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/fractals/fractal.h b/apps/plugins/fractals/fractal.h
index b64bf942fe..8e9446df0d 100644
--- a/apps/plugins/fractals/fractal.h
+++ b/apps/plugins/fractals/fractal.h
@@ -504,6 +504,9 @@
#define FRACTAL_PRECISION_DEC BUTTON_BACK
#define FRACTAL_RESET BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define FRACTAL_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/imageviewer/imageviewer_button.h b/apps/plugins/imageviewer/imageviewer_button.h
index d588de95ca..e6cd2ac089 100644
--- a/apps/plugins/imageviewer/imageviewer_button.h
+++ b/apps/plugins/imageviewer/imageviewer_button.h
@@ -539,6 +539,9 @@
#define IMGVIEW_MENU BUTTON_POWER
#define IMGVIEW_SLIDE_SHOW BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/invadrox.c b/apps/plugins/invadrox.c
index c28ab7e830..5eedb6ffc5 100644
--- a/apps/plugins/invadrox.c
+++ b/apps/plugins/invadrox.c
@@ -296,6 +296,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define RIGHT BUTTON_RIGHT
#define FIRE BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error INVADROX: Unsupported keypad
#endif
@@ -648,6 +651,23 @@ CONFIG_KEYPAD == MROBE500_PAD
#define LIVES_X 8
#define MAX_Y 15
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+
+/* Shanling Q1
+ */
+#define ARCADISH_GRAPHICS
+#define PLAYFIELD_X 32
+#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
+#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
+/* Redefine SCORE_Y */
+#undef SCORE_Y
+#define SCORE_Y 80
+#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
+#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
+#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
+#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
+#define LIVES_X 10
+#define MAX_Y 18
#else
#error INVADROX: Unsupported LCD type
diff --git a/apps/plugins/jewels.c b/apps/plugins/jewels.c
index 7f7965b07d..3f209ae556 100644
--- a/apps/plugins/jewels.c
+++ b/apps/plugins/jewels.c
@@ -377,6 +377,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define HK_SELECT "SELECT"
#define HK_CANCEL "BACK"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/lib/keymaps.h b/apps/plugins/lib/keymaps.h
index b660d4d85e..2cbca9e5ad 100644
--- a/apps/plugins/lib/keymaps.h
+++ b/apps/plugins/lib/keymaps.h
@@ -255,6 +255,15 @@
#define BTN_FIRE BUTTON_SELECT
#define BTN_PAUSE BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define BTN_FIRE BUTTON_CENTER
+#define BTN_PAUSE BUTTON_POWER
+#define BTN_HAVE_DIAGONAL
+#define BTN_DOWN_LEFT BUTTON_BOTTOMLEFT
+#define BTN_DOWN_RIGHT BUTTON_BOTTOMRIGHT
+#define BTN_UP_LEFT BUTTON_TOPLEFT
+#define BTN_UP_RIGHT BUTTON_TOPRIGHT
+
#else
#error Unsupported keypad
#endif
@@ -272,7 +281,8 @@
#elif (CONFIG_KEYPAD != COWON_D2_PAD) && \
(CONFIG_KEYPAD != DX50_PAD) && \
(CONFIG_KEYPAD != ONDAVX777_PAD) && \
- (CONFIG_KEYPAD != CREATIVE_ZENXFI2_PAD)
+ (CONFIG_KEYPAD != CREATIVE_ZENXFI2_PAD) && \
+ (CONFIG_KEYPAD != SHANLING_Q1_PAD)
#define BTN_FIRE BUTTON_BOTTOMLEFT
#define BTN_PAUSE BUTTON_TOPLEFT
#endif
diff --git a/apps/plugins/lib/pluginlib_actions.c b/apps/plugins/lib/pluginlib_actions.c
index 028472d9a8..4115177eaa 100644
--- a/apps/plugins/lib/pluginlib_actions.c
+++ b/apps/plugins/lib/pluginlib_actions.c
@@ -499,6 +499,8 @@ const struct button_mapping pla_main_ctx[] =
{PLA_SELECT, BUTTON_SELECT, BUTTON_NONE},
{PLA_SELECT_REL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{PLA_SELECT_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
+#elif (CONFIG_KEYPAD == SHANLING_Q1_PAD)
+ {PLA_EXIT, BUTTON_POWER, BUTTON_NONE},
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No actions defined
diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c
index a28d1d3862..6345d3c741 100644
--- a/apps/plugins/midi/midiplay.c
+++ b/apps/plugins/midi/midiplay.c
@@ -325,6 +325,9 @@
#define MIDI_VOL_DOWN BUTTON_VOL_DOWN
#define MIDI_PLAYPAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/minesweeper.c b/apps/plugins/minesweeper.c
index 19a6b99f12..a3c718d79f 100644
--- a/apps/plugins/minesweeper.c
+++ b/apps/plugins/minesweeper.c
@@ -443,6 +443,9 @@ CONFIG_KEYPAD == MROBE500_PAD
# define MINESWP_DISCOVER (BUTTON_SELECT|BUTTON_REPEAT)
# define MINESWP_INFO BUTTON_MENU
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mp3_encoder.c b/apps/plugins/mp3_encoder.c
index db10185c91..99671815ca 100644
--- a/apps/plugins/mp3_encoder.c
+++ b/apps/plugins/mp3_encoder.c
@@ -2580,6 +2580,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define MP3ENC_DONE BUTTON_POWER
#define MP3ENC_SELECT BUTTON_SELECT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mpegplayer/mpeg_settings.c b/apps/plugins/mpegplayer/mpeg_settings.c
index 505f3aa33d..b1445781d0 100644
--- a/apps/plugins/mpegplayer/mpeg_settings.c
+++ b/apps/plugins/mpegplayer/mpeg_settings.c
@@ -354,6 +354,9 @@ struct mpeg_settings settings;
#define MPEG_START_TIME_DOWN BUTTON_DOWN
#define MPEG_START_TIME_EXIT BUTTON_POWER
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define MPEG_START_TIME_EXIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index 34eafd5d9c..e66b4df146 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -486,6 +486,9 @@ CONFIG_KEYPAD == SANSA_M200_PAD
#define MPEG_RW BUTTON_LEFT
#define MPEG_FF BUTTON_RIGHT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index 881295d6ab..ae84e14f7f 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -548,6 +548,9 @@
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/pacbox/pacbox.h b/apps/plugins/pacbox/pacbox.h
index a3d42b099c..aa8c41fa95 100644
--- a/apps/plugins/pacbox/pacbox.h
+++ b/apps/plugins/pacbox/pacbox.h
@@ -398,6 +398,16 @@
#define PACMAN_1UP BUTTON_VOL_UP
#define PACMAN_COIN BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define PACMAN_UP BUTTON_TOPMIDDLE
+#define PACMAN_DOWN BUTTON_BOTTOMMIDDLE
+#define PACMAN_LEFT BUTTON_MIDLEFT
+#define PACMAN_RIGHT BUTTON_MIDRIGHT
+#define PACMAN_MENU BUTTON_TOPLEFT
+#define PACMAN_1UP BUTTON_BOTTOMLEFT
+#define PACMAN_2UP BUTTON_BOTTOMRIGHT
+#define PACMAN_COIN BUTTON_CENTER
+
#else
#error Keymap not defined!
diff --git a/apps/plugins/pegbox.c b/apps/plugins/pegbox.c
index f089c38023..cb6361547d 100644
--- a/apps/plugins/pegbox.c
+++ b/apps/plugins/pegbox.c
@@ -711,6 +711,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define LVL_UP_TEXT "VOL+"
#define LVL_DOWN_TEXT "VOL-"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "Unsupported keymap!"
#endif
diff --git a/apps/plugins/pong.c b/apps/plugins/pong.c
index 22484d0bc9..4a2c48e94c 100644
--- a/apps/plugins/pong.c
+++ b/apps/plugins/pong.c
@@ -325,6 +325,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PONG_RIGHT_UP BUTTON_BACK
#define PONG_RIGHT_DOWN BUTTON_RIGHT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/reversi/reversi-gui.h b/apps/plugins/reversi/reversi-gui.h
index 7e031e6103..926a71e1a2 100644
--- a/apps/plugins/reversi/reversi-gui.h
+++ b/apps/plugins/reversi/reversi-gui.h
@@ -361,6 +361,9 @@
#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
#define REVERSI_BUTTON_MENU BUTTON_MENU
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/rockblox.c b/apps/plugins/rockblox.c
index 07a15bfb62..927710b37b 100644
--- a/apps/plugins/rockblox.c
+++ b/apps/plugins/rockblox.c
@@ -465,6 +465,9 @@
#define ROCKBLOX_DROP BUTTON_PLAY
#define ROCKBLOX_RESTART BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -534,6 +537,22 @@
#define LEVEL_Y 142
#define LINES_Y 218
+#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
+
+#define BLOCK_WIDTH 19
+#define BLOCK_HEIGHT 19
+#define BOARD_X 27
+#define BOARD_Y 0
+#define LABEL_X 258
+#define SCORE_Y 40
+#define LEVEL_Y 92
+#define LINES_Y 140
+#define HIGH_LABEL_X 258
+#define HIGH_SCORE_Y 200
+#define HIGH_LEVEL_Y 258
+#define PREVIEW_X 258
+#define PREVIEW_Y 300
+
#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
#define BLOCK_WIDTH 12
diff --git a/apps/plugins/rockboy/rockboy.c b/apps/plugins/rockboy/rockboy.c
index d2c56ff6cc..2c5c6e4dbf 100644
--- a/apps/plugins/rockboy/rockboy.c
+++ b/apps/plugins/rockboy/rockboy.c
@@ -468,6 +468,9 @@ static void setoptions (void)
options.SELECT = BUTTON_VOL_UP;
options.MENU = BUTTON_POWER;
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+ /* use touchscreen */
+
#else
#error No Keymap Defined!
#endif
diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c
index 0d3211d4d2..98fdb468d3 100644
--- a/apps/plugins/rockpaint.c
+++ b/apps/plugins/rockpaint.c
@@ -404,6 +404,9 @@
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "Please define keys for this keypad"
#endif
diff --git a/apps/plugins/sliding_puzzle.c b/apps/plugins/sliding_puzzle.c
index d1820b2f50..a34cb77669 100644
--- a/apps/plugins/sliding_puzzle.c
+++ b/apps/plugins/sliding_puzzle.c
@@ -357,6 +357,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PUZZLE_SHUFFLE BUTTON_BACK
#define PUZZLE_PICTURE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/snake.c b/apps/plugins/snake.c
index 459d345fa2..25c89b264b 100644
--- a/apps/plugins/snake.c
+++ b/apps/plugins/snake.c
@@ -313,6 +313,9 @@ dir is the current direction of the snake - 0=up, 1=right, 2=down, 3=left;
#define SNAKE_DOWN BUTTON_DOWN
#define SNAKE_PLAYPAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/snake2.c b/apps/plugins/snake2.c
index 1536840daf..094fd854eb 100644
--- a/apps/plugins/snake2.c
+++ b/apps/plugins/snake2.c
@@ -62,6 +62,18 @@ Head and Tail are stored
#define TOP_X4 548 /* x-coord of the lowerright item (hi-score) */
#define TOP_Y1 8 /* y-coord of the top row of items */
#define TOP_Y2 50 /* y-coord of the bottom row of items */
+#elif (LCD_WIDTH >= 360) && (LCD_HEIGHT >= 400)
+ #define MULTIPLIER 12 /*Modifier for porting on other screens*/
+ #define MODIFIER_1 12
+ #define MODIFIER_2 10
+ #define CENTER_X 12
+ #define CENTER_Y 40
+ #define TOP_X1 34 /* x-coord of the upperleft item (game type) */
+ #define TOP_X2 320 /* x-coord of the upperright item (maze type) */
+ #define TOP_X3 42 /* x-coord of the lowerleft item (speed) */
+ #define TOP_X4 314 /* x-coord of the lowerright item (hi-score) */
+ #define TOP_Y1 4 /* y-coord of the top row of items */
+ #define TOP_Y2 25 /* y-coord of the bottom row of items */
#elif (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240)
#define MULTIPLIER 10 /*Modifier for porting on other screens*/
#define MODIFIER_1 10
@@ -446,6 +458,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "PLAY"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/sokoban.c b/apps/plugins/sokoban.c
index 41c671a38c..247663a5c2 100644
--- a/apps/plugins/sokoban.c
+++ b/apps/plugins/sokoban.c
@@ -696,6 +696,9 @@
#define BUTTON_SAVE BUTTON_BACK
#define BUTTON_SAVE_NAME "BACK"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/solitaire.c b/apps/plugins/solitaire.c
index 2d737df678..a48bfbc9be 100644
--- a/apps/plugins/solitaire.c
+++ b/apps/plugins/solitaire.c
@@ -741,6 +741,9 @@ CONFIG_KEYPAD == MROBE500_PAD
# define HK_CUR2STACK "HOLD SELECT"
# define HK_REM2STACK "VOL+"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+# define SOL_QUIT BUTTON_POWER
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/spacerocks.c b/apps/plugins/spacerocks.c
index 2d39c26b3d..8203fad612 100644
--- a/apps/plugins/spacerocks.c
+++ b/apps/plugins/spacerocks.c
@@ -372,6 +372,9 @@
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/star.c b/apps/plugins/star.c
index c186474ae3..874afc1cf1 100644
--- a/apps/plugins/star.c
+++ b/apps/plugins/star.c
@@ -668,6 +668,10 @@
#define STAR_LEVEL_DOWN_NAME "VOL-"
#define STAR_LEVEL_REPEAT_NAME "BACK"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define STAR_QUIT BUTTON_POWER
+#define STAR_QUIT_NAME "POWER"
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/stopwatch.lua b/apps/plugins/stopwatch.lua
index 578ba7f42c..34a3c57d72 100644
--- a/apps/plugins/stopwatch.lua
+++ b/apps/plugins/stopwatch.lua
@@ -280,7 +280,7 @@ function arrangeButtons(btns)
end
end
-rb.touchscreen_set_mode(rb.TOUCHSCREEN_POINT)
+rb.touchscreen_mode(rb.TOUCHSCREEN_POINT)
LapsView:init()
diff --git a/apps/plugins/sudoku/sudoku.h b/apps/plugins/sudoku/sudoku.h
index 1332a9a80f..e06581fdc9 100644
--- a/apps/plugins/sudoku/sudoku.h
+++ b/apps/plugins/sudoku/sudoku.h
@@ -460,6 +460,9 @@
#define SUDOKU_BUTTON_MENU BUTTON_MENU
#define SUDOKU_BUTTON_POSSIBLE BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/test_touchscreen.c b/apps/plugins/test_touchscreen.c
index 120ca8ac34..0d8e91f6a9 100644
--- a/apps/plugins/test_touchscreen.c
+++ b/apps/plugins/test_touchscreen.c
@@ -37,12 +37,17 @@
#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
#define TOUCHSCREEN_QUIT BUTTON_POWER
#define TOUCHSCREEN_TOGGLE BUTTON_MENU
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define TOUCHSCREEN_QUIT BUTTON_POWER
+#define TOUCHSCREEN_TOGGLE BUTTON_PLAY
#elif (CONFIG_KEYPAD == ANDROID_PAD)
#define TOUCHSCREEN_QUIT BUTTON_BACK
#define TOUCHSCREEN_TOGGLE BUTTON_MENU
#elif (CONFIG_KEYPAD == SDL_PAD)
#define TOUCHSCREEN_QUIT BUTTON_MIDLEFT
#define TOUCHSCREEN_TOGGLE BUTTON_CENTER
+#else
+# error "No keymap defined!"
#endif
/* plugin entry point */
diff --git a/apps/plugins/text_viewer/tv_button.h b/apps/plugins/text_viewer/tv_button.h
index d9a57d114f..4d45fbba03 100644
--- a/apps/plugins/text_viewer/tv_button.h
+++ b/apps/plugins/text_viewer/tv_button.h
@@ -572,6 +572,9 @@
#define TV_LINE_DOWN BUTTON_SCROLL_FWD
#define TV_BOOKMARK BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+#define TV_BOOKMARK BUTTON_PLAY
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/vu_meter.c b/apps/plugins/vu_meter.c
index e24ad8dcdc..356a7fdd93 100644
--- a/apps/plugins/vu_meter.c
+++ b/apps/plugins/vu_meter.c
@@ -452,6 +452,9 @@
#define LABEL_MENU "MENU"
#define LABEL_VOLUME "VOL+/VOL-"
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
diff --git a/apps/plugins/wormlet.c b/apps/plugins/wormlet.c
index d76f6a7d5a..162cea6208 100644
--- a/apps/plugins/wormlet.c
+++ b/apps/plugins/wormlet.c
@@ -407,6 +407,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define BTN_QUIT BUTTON_POWER
#define BTN_STOPRESET BUTTON_BACK
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error No keymap defined!
#endif
@@ -492,7 +495,8 @@ CONFIG_KEYPAD == MROBE500_PAD
#define SPEED 4
#define MAX_WORM_SEGMENTS 512
#elif ((LCD_WIDTH == 320) && (LCD_HEIGHT == 240)) || \
- ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400)))
+ ((LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400))) || \
+ ((LCD_WIDTH == 360) && (LCD_HEIGHT == 400))
#define FOOD_SIZE 7
#define ARGH_SIZE 8
#define SPEED 4
diff --git a/apps/plugins/xobox.c b/apps/plugins/xobox.c
index cf959ad7f5..b8b1964db4 100644
--- a/apps/plugins/xobox.c
+++ b/apps/plugins/xobox.c
@@ -351,6 +351,9 @@ CONFIG_KEYPAD == MROBE500_PAD
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error "No keymap defined!"
#endif
diff --git a/apps/plugins/zxbox/keymaps.h b/apps/plugins/zxbox/keymaps.h
index e9316a301b..e95a1d8c94 100644
--- a/apps/plugins/zxbox/keymaps.h
+++ b/apps/plugins/zxbox/keymaps.h
@@ -290,6 +290,9 @@
#define ZX_UP BUTTON_UP
#define ZX_DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
+/* use touchscreen */
+
#else
#error Keymap not defined!
diff --git a/apps/settings_list.c b/apps/settings_list.c
index c13df734e6..1cdbc4115d 100644
--- a/apps/settings_list.c
+++ b/apps/settings_list.c
@@ -242,6 +242,9 @@ static const char graphic_numeric[] = "graphic,numeric";
#define DEFAULT_FONT_HEIGHT 12
#elif LCD_HEIGHT <= 320
#define DEFAULT_FONT_HEIGHT 15
+#elif defined(SHANLING_Q1)
+ /* 16pt font looks pretty aliased & ugly */
+ #define DEFAULT_FONT_HEIGHT 18
#elif LCD_HEIGHT <= 400
#define DEFAULT_FONT_HEIGHT 16
#elif LCD_HEIGHT <= 480 && LCD_WIDTH < 800
@@ -261,7 +264,7 @@ static const char graphic_numeric[] = "graphic,numeric";
#endif
#ifdef HAVE_LCD_COLOR
- #if DEFAULT_FONT_HEIGHT >= 31
+ #if DEFAULT_FONT_HEIGHT >= 31 || defined(SHANLING_Q1)
#define DEFAULT_ICONSET "tango_icons.32x32"
#define DEFAULT_VIEWERS_ICONSET "tango_icons_viewers.32x32"
#elif DEFAULT_FONT_HEIGHT >= 23
@@ -848,7 +851,11 @@ const struct settings_list settings[] = {
#ifdef AUDIOHW_HAVE_FILTER_ROLL_OFF
CHOICE_SETTING(F_SOUNDSETTING, roll_off, LANG_FILTER_ROLL_OFF, 0,
-#if defined(AUDIOHW_HAVE_SHORT2_ROLL_OFF)
+#if defined(AUDIOHW_HAVE_ES9218_ROLL_OFF)
+ "roll_off", "linear fast,linear slow,minimum fast,minimum slow,apodizing 1,apodizing 2,hybrid fast,brick wall", sound_set_filter_roll_off,
+ 8, ID2P(LANG_FILTER_LINEAR_FAST), ID2P(LANG_FILTER_LINEAR_SLOW), ID2P(LANG_FILTER_MINIMUM_FAST), ID2P(LANG_FILTER_MINIMUM_SLOW),
+ ID2P(LANG_FILTER_APODIZING_1), ID2P(LANG_FILTER_APODIZING_2), ID2P(LANG_FILTER_HYBRID_FAST), ID2P(LANG_FILTER_BRICK_WALL)),
+#elif defined(AUDIOHW_HAVE_SHORT2_ROLL_OFF)
"roll_off", "sharp,slow,short sharp,short slow", sound_set_filter_roll_off,
4, ID2P(LANG_FILTER_SHARP), ID2P(LANG_FILTER_SLOW), ID2P(LANG_FILTER_SHORT_SHARP), ID2P(LANG_FILTER_SHORT_SLOW)),
#elif defined(AUDIOHW_HAVE_SHORT_ROLL_OFF)
diff --git a/backdrops/cabbiev2.360x400x16.bmp b/backdrops/cabbiev2.360x400x16.bmp
new file mode 100644
index 0000000000..e007b185d1
Binary files /dev/null and b/backdrops/cabbiev2.360x400x16.bmp differ
diff --git a/bootloader/SOURCES b/bootloader/SOURCES
index f72c58a0b7..6cdcd41ece 100644
--- a/bootloader/SOURCES
+++ b/bootloader/SOURCES
@@ -89,6 +89,6 @@ show_logo.c
#elif defined(SANSA_CONNECT)
sansaconnect.c
show_logo.c
-#elif defined(FIIO_M3K)
+#elif defined(FIIO_M3K) || defined(SHANLING_Q1)
x1000.c
#endif
diff --git a/bootloader/x1000.c b/bootloader/x1000.c
index 2c61773b11..3467547684 100644
--- a/bootloader/x1000.c
+++ b/bootloader/x1000.c
@@ -66,6 +66,17 @@
# define BL_SELECT_NAME "PLAY"
# define BL_QUIT_NAME "POWER"
# define BOOTBACKUP_FILE "/fiiom3k-boot.bin"
+#elif defined(SHANLING_Q1)
+# define BL_RECOVERY BUTTON_NEXT
+# define BL_UP BUTTON_PREV
+# define BL_DOWN BUTTON_NEXT
+# define BL_SELECT BUTTON_PLAY
+# define BL_QUIT BUTTON_POWER
+# define BL_UP_NAME "PREV"
+# define BL_DOWN_NAME "NEXT"
+# define BL_SELECT_NAME "PLAY"
+# define BL_QUIT_NAME "POWER"
+# define BOOTBACKUP_FILE "/shanlingq1-boot.bin"
#else
# error "Missing keymap!"
#endif
diff --git a/docs/CREDITS b/docs/CREDITS
index 7dee2ce527..613aa1f660 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -706,6 +706,7 @@ Caleb Connolly
Spencer Brennessel
Dana Conrad
Albert Song
+Marc Aarts
The libmad team
The wavpack team
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 2414682bb4..e055558b57 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -503,6 +503,8 @@ drivers/audio/pcm1792.c
drivers/audio/cs4398.c
#elif defined (HAVE_ES9018)
drivers/audio/es9018.c
+#elif defined (HAVE_ES9218)
+drivers/audio/es9218.c
#endif /* defined(HAVE_*) */
#else /* PLATFORM_HOSTED */
#if defined(SAMSUNG_YPR0) && defined(HAVE_AS3514)
@@ -1716,6 +1718,15 @@ target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
#endif /* FIIO_M3K */
+#if defined(SHANLING_Q1)
+target/mips/ingenic_x1000/shanlingq1/audiohw-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/backlight-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/lcd-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c
+target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c
+#endif /* SHANLING_Q1 */
+
#if defined(LYRE_PROTO1)
target/arm/at91sam/lyre_proto1/adc-lyre_proto1.c
target/arm/at91sam/lyre_proto1/backlight-lyre_proto1.c
@@ -1948,6 +1959,9 @@ drivers/axp-pmu.c
#ifdef HAVE_FT6x06
drivers/ft6x06.c
#endif
+#ifdef HAVE_CW2015
+drivers/cw2015.c
+#endif
#endif
/* firmware/kernel section */
diff --git a/firmware/drivers/audio/es9218.c b/firmware/drivers/audio/es9218.c
new file mode 100644
index 0000000000..76d387221a
--- /dev/null
+++ b/firmware/drivers/audio/es9218.c
@@ -0,0 +1,226 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "audiohw.h"
+#include "system.h"
+#include "i2c-async.h"
+
+struct es9218_state {
+ enum es9218_clock_gear clk_gear;
+ uint32_t fsr;
+ uint32_t nco;
+};
+
+static struct es9218_state es9218;
+
+void es9218_open(void)
+{
+ /* Enable power supply */
+ es9218_set_power_pin(1);
+
+ /* "Wiggle" reset pin to get the internal oscillator to stabilize.
+ * This should also work if using an external powered oscillator,
+ * although in that case it's unnecessary to do this dance. */
+ es9218_set_reset_pin(1);
+ udelay(75);
+ es9218_set_reset_pin(0);
+ udelay(50);
+ es9218_set_reset_pin(1);
+ mdelay(2);
+
+ /* Initialize driver state */
+ es9218.clk_gear = ES9218_CLK_GEAR_1;
+ es9218.fsr = 0;
+ es9218.nco = 0;
+}
+
+void es9218_close(void)
+{
+ /* Turn off power supply */
+ es9218_set_power_pin(0);
+ es9218_set_reset_pin(0);
+}
+
+static void recalc_nco(void)
+{
+ /* nco * CLK *
+ * fsr = --------- *
+ * 2**32 */
+
+ uint32_t clk = es9218_get_mclk();
+ clk >>= (int)es9218.clk_gear;
+
+ uint64_t nco64 = es9218.fsr;
+ nco64 <<= 32;
+ nco64 /= clk;
+
+ /* let's just ignore overflow... */
+ uint32_t nco = nco64;
+ if(nco != es9218.nco) {
+ es9218.nco = nco;
+
+ /* registers must be written in this order */
+ es9218_write(ES9218_REG_PROG_NCO_BIT0_7, (nco >> 0) & 0xff);
+ es9218_write(ES9218_REG_PROG_NCO_BIT8_15, (nco >> 8) & 0xff);
+ es9218_write(ES9218_REG_PROG_NCO_BIT16_23, (nco >> 16) & 0xff);
+ es9218_write(ES9218_REG_PROG_NCO_BIT24_31, (nco >> 24) & 0xff);
+ }
+}
+
+void es9218_set_clock_gear(enum es9218_clock_gear gear)
+{
+ if(gear != es9218.clk_gear) {
+ es9218.clk_gear = gear;
+ es9218_update(ES9218_REG_SYSTEM, 0x0c, (uint8_t)(gear & 3) << 2);
+ recalc_nco();
+ }
+}
+
+void es9218_set_nco_frequency(uint32_t fsr)
+{
+ if(fsr != es9218.fsr) {
+ es9218.fsr = fsr;
+ recalc_nco();
+ }
+}
+
+void es9218_recompute_nco(void)
+{
+ recalc_nco();
+}
+
+void es9218_set_amp_mode(enum es9218_amp_mode mode)
+{
+ es9218_update(ES9218_REG_AMP_CONFIG, 0x03, (uint8_t)mode & 3);
+}
+
+void es9218_set_amp_powered(bool en)
+{
+ /* this doesn't seem to be necessary..? */
+ es9218_update(ES9218_REG_ANALOG_CTRL, 0x40, en ? 0x40 : 0x00);
+}
+
+void es9218_set_iface_role(enum es9218_iface_role role)
+{
+ /* asrc is used to lock onto the incoming audio frequency and is
+ * only used in aysnchronous slave mode. In synchronous operation,
+ * including master mode, it can be disabled to save power. */
+ int asrc_en = (role == ES9218_IFACE_ROLE_SLAVE ? 1 : 0);
+ int master_mode = (role == ES9218_IFACE_ROLE_MASTER ? 1 : 0);
+
+ es9218_update(ES9218_REG_MASTER_MODE_CONFIG, 1 << 7, master_mode << 7);
+ es9218_update(ES9218_REG_GENERAL_CONFIG, 1 << 7, asrc_en << 7);
+}
+
+void es9218_set_iface_format(enum es9218_iface_format fmt,
+ enum es9218_iface_bits bits)
+{
+ uint8_t val = 0;
+ val |= ((uint8_t)bits & 3) << 6;
+ val |= ((uint8_t)fmt & 3) << 4;
+ /* keep low 4 bits zero -> use normal I2S mode, disable DSD mode */
+ es9218_write(ES9218_REG_INPUT_SEL, val);
+}
+
+static int dig_vol_to_hw(int x)
+{
+ x = MIN(x, ES9218_DIG_VOLUME_MAX);
+ x = MAX(x, ES9218_DIG_VOLUME_MIN);
+ return 0xff - (x - ES9218_DIG_VOLUME_MIN) / ES9218_DIG_VOLUME_STEP;
+}
+
+static int amp_vol_to_hw(int x)
+{
+ x = MIN(x, ES9218_AMP_VOLUME_MAX);
+ x = MAX(x, ES9218_AMP_VOLUME_MIN);
+ return 24 - (x - ES9218_AMP_VOLUME_MIN) / ES9218_AMP_VOLUME_STEP;
+}
+
+void es9218_set_dig_volume(int vol_l, int vol_r)
+{
+ es9218_write(ES9218_REG_VOLUME_LEFT, dig_vol_to_hw(vol_l));
+ es9218_write(ES9218_REG_VOLUME_RIGHT, dig_vol_to_hw(vol_r));
+}
+
+void es9218_set_amp_volume(int vol)
+{
+ es9218_update(ES9218_REG_ANALOG_VOL, 0x1f, amp_vol_to_hw(vol));
+}
+
+void es9218_mute(bool en)
+{
+ es9218_update(ES9218_REG_FILTER_SYS_MUTE, 1, en ? 1 : 0);
+}
+
+void es9218_set_filter(enum es9218_filter_type filt)
+{
+ es9218_update(ES9218_REG_FILTER_SYS_MUTE, 0xe0, ((int)filt & 7) << 5);
+}
+
+void es9218_set_automute_time(int time)
+{
+ if(time < 0) time = 0;
+ if(time > 255) time = 255;
+ es9218_write(ES9218_REG_AUTOMUTE_TIME, time);
+}
+
+void es9218_set_automute_level(int dB)
+{
+ es9218_update(ES9218_REG_AUTOMUTE_LEVEL, 0x7f, dB);
+}
+
+void es9218_set_automute_fast_mode(bool en)
+{
+ es9218_update(ES9218_REG_MIX_AUTOMUTE, 0x10, en ? 0x10 : 0x00);
+}
+
+void es9218_set_dpll_bandwidth(int knob)
+{
+ es9218_update(ES9218_REG_ASRC_DPLL_BANDWIDTH, 0xf0, (knob & 0xf) << 4);
+}
+
+void es9218_set_thd_compensation(bool en)
+{
+ es9218_update(ES9218_REG_THD_COMP_BYPASS, 0x40, en ? 0x40 : 0);
+}
+
+void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3)
+{
+ es9218_write(ES9218_REG_THD_COMP_C2_LO, c2 & 0xff);
+ es9218_write(ES9218_REG_THD_COMP_C2_HI, (c2 >> 8) & 0xff);
+ es9218_write(ES9218_REG_THD_COMP_C3_LO, c3 & 0xff);
+ es9218_write(ES9218_REG_THD_COMP_C3_HI, (c3 >> 8) & 0xff);
+}
+
+int es9218_read(int reg)
+{
+ return i2c_reg_read1(ES9218_BUS, ES9218_ADDR, reg);
+}
+
+void es9218_write(int reg, uint8_t val)
+{
+ i2c_reg_write1(ES9218_BUS, ES9218_ADDR, reg, val);
+}
+
+void es9218_update(int reg, uint8_t msk, uint8_t val)
+{
+ i2c_reg_modify1(ES9218_BUS, ES9218_ADDR, reg, msk, val, NULL);
+}
diff --git a/firmware/drivers/cw2015.c b/firmware/drivers/cw2015.c
new file mode 100644
index 0000000000..705ca16e22
--- /dev/null
+++ b/firmware/drivers/cw2015.c
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "cw2015.h"
+#include "i2c-async.h"
+#include
+#include "system.h"
+
+/* Headers for the debug menu */
+#ifndef BOOTLOADER
+# include "action.h"
+# include "list.h"
+# include
+#endif
+
+/* Battery profile info is an opaque blob. According to this,
+ * https://lore.kernel.org/linux-pm/20200503154855.duwj2djgqfiyleq5@earth.universe/T/#u
+ * the blob only comes from Cellwise testing a physical battery and cannot be
+ * obtained any other way. It's specific to a given battery so each target has
+ * its own profile.
+ *
+ * Profile data seems to be retained on the chip so it's not a hard requirement
+ * to define this. Provided you don't lose power in the meantime, it should be
+ * enough to just boot the OF, then boot Rockbox and read out the battery info
+ * from the CW2015 debug screen.
+ */
+#if defined(SHANLING_Q1)
+static const uint8_t device_batinfo[CW2015_SIZE_BATINFO] = {
+ 0x15, 0x7E, 0x61, 0x59, 0x57, 0x55, 0x56, 0x4C,
+ 0x4E, 0x4D, 0x50, 0x4C, 0x45, 0x3A, 0x2D, 0x27,
+ 0x22, 0x1E, 0x19, 0x1E, 0x2A, 0x3C, 0x48, 0x45,
+ 0x1D, 0x94, 0x08, 0xF6, 0x15, 0x29, 0x48, 0x51,
+ 0x5D, 0x60, 0x63, 0x66, 0x45, 0x1D, 0x83, 0x38,
+ 0x09, 0x43, 0x16, 0x42, 0x76, 0x98, 0xA5, 0x1B,
+ 0x41, 0x76, 0x99, 0xBF, 0x80, 0xC0, 0xEF, 0xCB,
+ 0x2F, 0x00, 0x64, 0xA5, 0xB5, 0x0E, 0x30, 0x29,
+};
+#else
+# define NO_BATINFO
+#endif
+
+static uint8_t chip_batinfo[CW2015_SIZE_BATINFO];
+
+/* TODO: Finish implementing this
+ *
+ * Although this chip might give a better battery estimate than voltage does,
+ * the mainline linux driver has a lot of weird hacks due to oddities like the
+ * SoC getting stuck during charging, and from limited testing it seems this
+ * may occur for the Q1 too.
+ */
+
+static int cw2015_read_bat_info(uint8_t* data)
+{
+ for(int i = 0; i < CW2015_SIZE_BATINFO; ++i) {
+ int r = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_BATINFO + i);
+ if(r < 0)
+ return r;
+
+ data[i] = r & 0xff;
+ }
+
+ return 0;
+}
+
+void cw2015_init(void)
+{
+ /* mdelay(100); */
+ int rc = cw2015_read_bat_info(&chip_batinfo[0]);
+ if(rc < 0)
+ memset(chip_batinfo, 0, sizeof(chip_batinfo));
+}
+
+int cw2015_get_vcell(void)
+{
+ int vcell_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL);
+ int vcell_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_VCELL+1);
+
+ if(vcell_msb < 0 || vcell_lsb < 0)
+ return -1;
+
+ /* 14 bits, resolution 305 uV */
+ int v_raw = ((vcell_msb & 0x3f) << 8) | vcell_lsb;
+ return v_raw * 61 / 200;
+}
+
+int cw2015_get_soc(void)
+{
+ int soc_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_SOC);
+
+ if(soc_msb < 0)
+ return -1;
+
+ /* MSB is the state of charge in percentage.
+ * the LSB contains fractional information not useful to Rockbox. */
+ return soc_msb & 0xff;
+}
+
+int cw2015_get_rrt(void)
+{
+ int rrt_msb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT);
+ int rrt_lsb = i2c_reg_read1(CW2015_BUS, CW2015_ADDR, CW2015_REG_RRT_ALERT+1);
+
+ if(rrt_msb < 0 || rrt_lsb < 0)
+ return -1;
+
+ /* 13 bits, resolution 1 minute */
+ return ((rrt_msb & 0x1f) << 8) | rrt_lsb;
+}
+
+const uint8_t* cw2015_get_bat_info(void)
+{
+ return &chip_batinfo[0];
+}
+
+#ifndef BOOTLOADER
+enum {
+ CW2015_DEBUG_VCELL = 0,
+ CW2015_DEBUG_SOC,
+ CW2015_DEBUG_RRT,
+ CW2015_DEBUG_BATINFO,
+ CW2015_DEBUG_BATINFO_LAST = CW2015_DEBUG_BATINFO + 7,
+ CW2015_DEBUG_NUM_ENTRIES,
+};
+
+static int cw2015_debug_menu_cb(int action, struct gui_synclist* lists)
+{
+ (void)lists;
+
+ if(action == ACTION_NONE)
+ action = ACTION_REDRAW;
+
+ return action;
+}
+
+static const char* cw2015_debug_menu_get_name(int item, void* data,
+ char* buf, size_t buflen)
+{
+ (void)data;
+
+ /* hexdump of battery info */
+ if(item >= CW2015_DEBUG_BATINFO && item <= CW2015_DEBUG_BATINFO_LAST) {
+ int i = item - CW2015_DEBUG_BATINFO;
+ const uint8_t* batinfo = cw2015_get_bat_info();
+ snprintf(buf, buflen, "BatInfo%d: %02x %02x %02x %02x %02x %02x %02x %02x", i,
+ batinfo[8*i + 0], batinfo[8*i + 1], batinfo[8*i + 2], batinfo[8*i + 3],
+ batinfo[8*i + 4], batinfo[8*i + 5], batinfo[8*i + 6], batinfo[8*i + 7]);
+ return buf;
+ }
+
+ switch(item) {
+ case CW2015_DEBUG_VCELL:
+ snprintf(buf, buflen, "VCell: %d mV", cw2015_get_vcell());
+ return buf;
+ case CW2015_DEBUG_SOC:
+ snprintf(buf, buflen, "SOC: %d%%", cw2015_get_soc());
+ return buf;
+ case CW2015_DEBUG_RRT:
+ snprintf(buf, buflen, "Runtime: %d min", cw2015_get_rrt());
+ return buf;
+ default:
+ return "---";
+ }
+}
+
+bool cw2015_debug_menu(void)
+{
+ struct simplelist_info info;
+ simplelist_info_init(&info, "CW2015 debug", CW2015_DEBUG_NUM_ENTRIES, NULL);
+ info.action_callback = cw2015_debug_menu_cb;
+ info.get_name = cw2015_debug_menu_get_name;
+ return simplelist_show_list(&info);
+}
+#endif
diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h
index ceafc6ebf7..5b2815149d 100644
--- a/firmware/export/audiohw.h
+++ b/firmware/export/audiohw.h
@@ -216,6 +216,8 @@ struct sound_settings_info
#include "cs4398.h"
#elif defined(HAVE_ES9018)
#include "es9018.h"
+#elif defined(HAVE_ES9218)
+#include "es9218.h"
#elif (CONFIG_PLATFORM & (PLATFORM_ANDROID | PLATFORM_MAEMO \
| PLATFORM_PANDORA | PLATFORM_SDL))
#include "hosted_codec.h"
diff --git a/firmware/export/config.h b/firmware/export/config.h
index fdf3bf420d..5e7b2be6e4 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -160,6 +160,7 @@
#define FIIO_M3K_LINUX_PAD 71
#define EROSQ_PAD 72
#define FIIO_M3K_PAD 73
+#define SHANLING_Q1_PAD 74
/* CONFIG_REMOTE_KEYPAD */
#define H100_REMOTE 1
@@ -274,6 +275,7 @@
#define LCD_IHIFI770C 67 /* as used by IHIFI 770C */
#define LCD_IHIFI800 68 /* as used by IHIFI 800 */
#define LCD_FIIOM3K 69 /* as used by the FiiO M3K */
+#define LCD_SHANLING_Q1 70 /* as used by the Shanling Q1 */
/* LCD_PIXELFORMAT */
#define HORIZONTAL_PACKING 1
@@ -592,6 +594,8 @@ Lyre prototype 1 */
#include "config/fiiom3k.h"
#elif defined(EROS_Q)
#include "config/aigoerosq.h"
+#elif defined(SHANLING_Q1)
+#include "config/shanlingq1.h"
#else
//#error "unknown hwardware platform!"
#endif
diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h
new file mode 100644
index 0000000000..88175b9160
--- /dev/null
+++ b/firmware/export/config/shanlingq1.h
@@ -0,0 +1,119 @@
+/* RoLo-related defines */
+#define MODEL_NAME "Shanling Q1"
+#define MODEL_NUMBER 115
+#define BOOTFILE_EXT "q1"
+#define BOOTFILE "rockbox." BOOTFILE_EXT
+#define BOOTDIR "/.rockbox"
+#define FIRMWARE_OFFSET_FILE_CRC 0
+#define FIRMWARE_OFFSET_FILE_DATA 8
+
+/* CPU defines */
+#define CONFIG_CPU X1000
+#define X1000_EXCLK_FREQ 24000000
+#define CPU_FREQ 1008000000
+
+#ifndef SIMULATOR
+#define TIMER_FREQ X1000_EXCLK_FREQ
+#endif
+
+/* Kernel defines */
+#define INCLUDE_TIMEOUT_API
+#define HAVE_SEMAPHORE_OBJECTS
+
+/* Drivers */
+#define HAVE_I2C_ASYNC
+
+/* Buffer for plugins and codecs. */
+#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+#define CODEC_SIZE 0x100000 /* 1 MiB */
+
+/* LCD defines */
+#define CONFIG_LCD LCD_SHANLING_Q1
+#define LCD_WIDTH 360
+#define LCD_HEIGHT 400
+#define LCD_DEPTH 16
+#define LCD_PIXELFORMAT RGB565
+#define LCD_DPI 200
+#define HAVE_LCD_COLOR
+#define HAVE_LCD_BITMAP
+#define HAVE_LCD_ENABLE
+#define LCD_X1000_FASTSLEEP
+#define LCD_X1000_DMA_WAIT_FOR_FRAME
+
+/* Backlight defines */
+#define HAVE_BACKLIGHT
+#define HAVE_BACKLIGHT_BRIGHTNESS
+#define MIN_BRIGHTNESS_SETTING 1
+#define MAX_BRIGHTNESS_SETTING 100
+#define BRIGHTNESS_STEP 5
+#define DEFAULT_BRIGHTNESS_SETTING 70
+#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING
+
+/* Codec / audio hardware defines */
+#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192
+#define HAVE_ES9218
+#define HAVE_SW_TONE_CONTROLS
+
+/* Button defines */
+#define CONFIG_KEYPAD SHANLING_Q1_PAD
+#define HAVE_TOUCHSCREEN
+#define HAVE_BUTTON_DATA
+#define HAVE_FT6x06
+#define HAVE_HEADPHONE_DETECTION
+
+/* Storage defines */
+#define CONFIG_STORAGE STORAGE_SD
+#define HAVE_HOTSWAP
+#define HAVE_HOTSWAP_STORAGE_AS_MAIN
+#define HAVE_MULTIDRIVE
+#define NUM_DRIVES 1
+#define STORAGE_WANTS_ALIGN
+#define STORAGE_NEEDS_BOUNCE_BUFFER
+
+/* RTC settings */
+#define CONFIG_RTC RTC_X1000
+/* TODO: implement HAVE_RTC_ALARM */
+
+/* Power management */
+#define CONFIG_BATTERY_MEASURE (VOLTAGE_MEASURE)
+#define CONFIG_CHARGING CHARGING_MONITOR
+#define HAVE_SW_POWEROFF
+
+#ifndef SIMULATOR
+/* TODO: get the CW2015 driver working correctly */
+/* #define HAVE_CW2015 */
+#define HAVE_AXP_PMU 192 /* Presumed */
+#define HAVE_POWEROFF_WHILE_CHARGING
+#endif
+
+/* Only one battery type */
+#define BATTERY_CAPACITY_DEFAULT 1100
+#define BATTERY_CAPACITY_MIN 1100
+#define BATTERY_CAPACITY_MAX 1100
+#define BATTERY_CAPACITY_INC 0
+#define BATTERY_TYPES_COUNT 1
+
+/* USB support */
+#ifndef SIMULATOR
+#define CONFIG_USBOTG USBOTG_DESIGNWARE
+#define USB_DW_ARCH_SLAVE
+#define USB_DW_TURNAROUND 5
+#define HAVE_USBSTACK
+#define USB_VENDOR_ID 0x0525 /* Same as the xDuuo X3, for some reason. */
+#define USB_PRODUCT_ID 0xa4a5 /* Nb. DAC mode uses 20b1:301f 'XMOS Ltd' */
+#define USB_DEVBSS_ATTR __attribute__((aligned(32)))
+#define HAVE_USB_POWER
+#define HAVE_USB_CHARGING_ENABLE
+#define HAVE_BOOTLOADER_USB_MODE
+#endif
+
+/* Rockbox capabilities */
+#define HAVE_FAT16SUPPORT
+#define HAVE_ALBUMART
+#define HAVE_BMP_SCALING
+#define HAVE_JPEG
+#define HAVE_TAGCACHE
+#define HAVE_VOLUME_IN_LIST
+#define HAVE_QUICKSCREEN
+#define HAVE_HOTKEY
+#define AB_REPEAT_ENABLE
diff --git a/firmware/export/cw2015.h b/firmware/export/cw2015.h
new file mode 100644
index 0000000000..c810d1b7b5
--- /dev/null
+++ b/firmware/export/cw2015.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 __CW2015_H__
+#define __CW2015_H__
+
+#include
+#include
+
+/* Driver for I2C battery fuel gauge IC CW2015. */
+
+#define CW2015_REG_VERSION 0x00
+#define CW2015_REG_VCELL 0x02 /* 14 bits, registers 0x02 - 0x03 */
+#define CW2015_REG_SOC 0x04 /* 16 bits, registers 0x04 - 0x05 */
+#define CW2015_REG_RRT_ALERT 0x06 /* 13 bit RRT + alert flag, 0x06-0x07 */
+#define CW2015_REG_CONFIG 0x08
+#define CW2015_REG_MODE 0x0a
+#define CW2015_REG_BATINFO 0x10 /* cf. mainline Linux CW2015 driver */
+#define CW2015_SIZE_BATINFO 64
+
+extern void cw2015_init(void);
+
+/* Read the battery terminal voltage, converted to millivolts. */
+extern int cw2015_get_vcell(void);
+
+/* Read the SOC, in percent (0-100%). */
+extern int cw2015_get_soc(void);
+
+/* Get the estimated remaining run time, in minutes.
+ * Not a linearly varying quantity, according to the datasheet. */
+extern int cw2015_get_rrt(void);
+
+/* Read the current battery profile */
+extern const uint8_t* cw2015_get_bat_info(void);
+
+/* Debug screen */
+extern bool cw2015_debug_menu(void);
+
+#endif /* __CW2015_H__ */
diff --git a/firmware/export/es9218.h b/firmware/export/es9218.h
new file mode 100644
index 0000000000..1492304c67
--- /dev/null
+++ b/firmware/export/es9218.h
@@ -0,0 +1,230 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 __ES9218_H__
+#define __ES9218_H__
+
+#include
+#include
+
+#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP)
+#define AUDIOHW_HAVE_ES9218_ROLL_OFF
+
+#define ES9218_DIG_VOLUME_MIN (-1275)
+#define ES9218_DIG_VOLUME_MAX 0
+#define ES9218_DIG_VOLUME_STEP 5
+
+#define ES9218_AMP_VOLUME_MIN (-240)
+#define ES9218_AMP_VOLUME_MAX 0
+#define ES9218_AMP_VOLUME_STEP 10
+
+AUDIOHW_SETTING(VOLUME, "dB", 1, ES9218_DIG_VOLUME_STEP,
+ ES9218_DIG_VOLUME_MIN, ES9218_DIG_VOLUME_MAX, -200)
+AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 7, 0)
+AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0)
+
+/* Register addresses */
+#define ES9218_REG_SYSTEM 0x00
+#define ES9218_REG_INPUT_SEL 0x01
+#define ES9218_REG_MIX_AUTOMUTE 0x02
+#define ES9218_REG_ANALOG_VOL 0x03
+#define ES9218_REG_AUTOMUTE_TIME 0x04
+#define ES9218_REG_AUTOMUTE_LEVEL 0x05
+#define ES9218_REG_DOP_VOLUME_RAMP 0x06
+#define ES9218_REG_FILTER_SYS_MUTE 0x07
+#define ES9218_REG_GPIO1_2_CONFIG 0x08
+#define ES9218_REG_RESERVED_1 0x09
+#define ES9218_REG_MASTER_MODE_CONFIG 0x0a
+#define ES9218_REG_OVERCURRENT_PROT 0x0b
+#define ES9218_REG_ASRC_DPLL_BANDWIDTH 0x0c
+#define ES9218_REG_THD_COMP_BYPASS 0x0d
+#define ES9218_REG_SOFT_START_CONFIG 0x0e
+#define ES9218_REG_VOLUME_LEFT 0x0f
+#define ES9218_REG_VOLUME_RIGHT 0x10
+#define ES9218_REG_MASTER_TRIM_BIT0_7 0x11
+#define ES9218_REG_MASTER_TRIM_BIT8_15 0x12
+#define ES9218_REG_MASTER_TRIM_BIT16_23 0x13
+#define ES9218_REG_MASTER_TRIM_BIT24_31 0x14
+#define ES9218_REG_GPIO_INPUT_SEL 0x15
+#define ES9218_REG_THD_COMP_C2_LO 0x16
+#define ES9218_REG_THD_COMP_C2_HI 0x17
+#define ES9218_REG_THD_COMP_C3_LO 0x18
+#define ES9218_REG_THD_COMP_C3_HI 0x19
+#define ES9218_REG_CHARGE_PUMP_SS_DELAY 0x1a
+#define ES9218_REG_GENERAL_CONFIG 0x1b
+#define ES9218_REG_RESERVED_2 0x1c
+#define ES9218_REG_GPIO_INV_CLOCK_GEAR 0x1d
+#define ES9218_REG_CHARGE_PUMP_CLK_LO 0x1e
+#define ES9218_REG_CHARGE_PUMP_CLK_HI 0x1f
+#define ES9218_REG_AMP_CONFIG 0x20
+#define ES9218_REG_INTERRUPT_MASK 0x21
+#define ES9218_REG_PROG_NCO_BIT0_7 0x22
+#define ES9218_REG_PROG_NCO_BIT8_15 0x23
+#define ES9218_REG_PROG_NCO_BIT16_23 0x24
+#define ES9218_REG_PROG_NCO_BIT24_31 0x25
+#define ES9218_REG_RESERVED_3 0x27
+#define ES9218_REG_FIR_RAM_ADDR 0x28
+#define ES9218_REG_FIR_DATA_BIT0_7 0x29
+#define ES9218_REG_FIR_DATA_BIT8_15 0x2a
+#define ES9218_REG_FIR_DATA_BIT16_23 0x2b
+#define ES9218_REG_PROG_FIR_CONFIG 0x2c
+#define ES9218_REG_ANALOG_OVERRIDE_1 0x2d
+#define ES9218_REG_ANALOG_OVERRIDE_2 0x2e
+#define ES9218_REG_ANALOG_OVERRIDE_3 0x2f
+#define ES9218_REG_ANALOG_CTRL 0x30
+#define ES9218_REG_CLKGEAR_CFG_BIT0_7 0x31
+#define ES9218_REG_CLKGEAR_CFG_BIT8_15 0x32
+#define ES9218_REG_CLKGEAR_CFG_BIT16_23 0x33
+#define ES9218_REG_RESERVED_4 0x34
+#define ES9218_REG_THD_COMP_C2_CH2_LO 0x35
+#define ES9218_REG_THD_COMP_C2_CH2_HI 0x36
+#define ES9218_REG_THD_COMP_C3_CH2_LO 0x37
+#define ES9218_REG_THD_COMP_C3_CH2_HI 0x38
+#define ES9218_REG_RESERVED_5 0x39
+#define ES9218_REG_RESERVED_6 0x3a
+#define ES9218_REG_RESERVED_7 0x3b
+#define ES9218_REG_RESERVED_8 0x3c
+#define ES9218_REG_CHIP_ID_AND_STATUS 0x40
+#define ES9218_REG_GPIO_AND_CLOCK_GEAR 0x41
+#define ES9218_REG_DPLL_NUMBER_BIT0_7 0x42
+#define ES9218_REG_DPLL_NUMBER_BIT8_15 0x43
+#define ES9218_REG_DPLL_NUMBER_BIT16_23 0x44
+#define ES9218_REG_DPLL_NUMBER_BIT24_31 0x45
+#define ES9218_REG_INPUT_MUTE_STATUS 0x48
+#define ES9218_REG_FIR_READ_BIT0_7 0x49
+#define ES9218_REG_FIR_READ_BIT8_15 0x4a
+#define ES9218_REG_FIR_READ_BIT16_23 0x4b
+
+enum es9218_clock_gear {
+ ES9218_CLK_GEAR_1 = 0, /* CLK = XI/1 */
+ ES9218_CLK_GEAR_2 = 1, /* CLK = XI/2 */
+ ES9218_CLK_GEAR_4 = 2, /* CLK = XI/4 */
+ ES9218_CLK_GEAR_8 = 3, /* CLK = XI/8 */
+};
+
+enum es9218_amp_mode {
+ ES9218_AMP_MODE_CORE_ON = 0,
+ ES9218_AMP_MODE_LOWFI = 1,
+ ES9218_AMP_MODE_1VRMS = 2,
+ ES9218_AMP_MODE_2VRMS = 3,
+};
+
+enum es9218_iface_role {
+ ES9218_IFACE_ROLE_SLAVE = 0,
+ ES9218_IFACE_ROLE_MASTER = 1,
+};
+
+enum es9218_iface_format {
+ ES9218_IFACE_FORMAT_I2S = 0,
+ ES9218_IFACE_FORMAT_LJUST = 1,
+ ES9218_IFACE_FORMAT_RJUST = 2,
+};
+
+enum es9218_iface_bits {
+ ES9218_IFACE_BITS_16 = 0,
+ ES9218_IFACE_BITS_24 = 1,
+ ES9218_IFACE_BITS_32 = 2,
+};
+
+enum es9218_filter_type {
+ ES9218_FILTER_LINEAR_FAST = 0,
+ ES9218_FILTER_LINEAR_SLOW = 1,
+ ES9218_FILTER_MINIMUM_FAST = 2,
+ ES9218_FILTER_MINIMUM_SLOW = 3,
+ ES9218_FILTER_APODIZING_1 = 4,
+ ES9218_FILTER_APODIZING_2 = 5,
+ ES9218_FILTER_HYBRID_FAST = 6,
+ ES9218_FILTER_BRICK_WALL = 7,
+};
+
+/* Power DAC on or off */
+extern void es9218_open(void);
+extern void es9218_close(void);
+
+/* Clock controls
+ *
+ * - Clock gear divides the input master clock to produce the DAC's clock.
+ * Frequency can be lowered to save power when using lower sample rates.
+ *
+ * - NCO (numerically controller oscillator), according to the datasheet,
+ * defines the ratio between the DAC's clock and the FSR (for PCM modes,
+ * this is I2S frame clock = sample rate). In master mode it effectively
+ * controls the sampling frequency by setting the I2S frame clock output.
+ * It can also be used in slave mode, but other parts of the datasheet
+ * say contradictory things about synchronous operation in slave mode.
+ *
+ * - If using NCO mode and a varying MCLK input (eg. input from the SoC) then
+ * you will need to call es9218_recompute_nco() when changing MCLK in order
+ * to refresh the NCO setting.
+ */
+extern void es9218_set_clock_gear(enum es9218_clock_gear gear);
+extern void es9218_set_nco_frequency(uint32_t fsr);
+extern void es9218_recompute_nco(void);
+
+/* Amplifier controls */
+extern void es9218_set_amp_mode(enum es9218_amp_mode mode);
+extern void es9218_set_amp_powered(bool en);
+
+/* Interface selection */
+extern void es9218_set_iface_role(enum es9218_iface_role role);
+extern void es9218_set_iface_format(enum es9218_iface_format fmt,
+ enum es9218_iface_bits bits);
+
+/* Volume controls, all volumes given in units of dB/10 */
+extern void es9218_set_dig_volume(int vol_l, int vol_r);
+extern void es9218_set_amp_volume(int vol);
+
+/* System mute */
+extern void es9218_mute(bool muted);
+
+/* Oversampling filter */
+extern void es9218_set_filter(enum es9218_filter_type filt);
+
+/* Automute settings */
+extern void es9218_set_automute_time(int time);
+extern void es9218_set_automute_level(int dB);
+extern void es9218_set_automute_fast_mode(bool en);
+
+/* DPLL bandwidth setting (knob = 0-15) */
+extern void es9218_set_dpll_bandwidth(int knob);
+
+/* THD compensation */
+extern void es9218_set_thd_compensation(bool en);
+extern void es9218_set_thd_coeffs(uint16_t c2, uint16_t c3);
+
+/* Direct register read/write/update operations */
+extern int es9218_read(int reg);
+extern void es9218_write(int reg, uint8_t val);
+extern void es9218_update(int reg, uint8_t msk, uint8_t val);
+
+/* GPIO pin setting callbacks */
+extern void es9218_set_power_pin(int level);
+extern void es9218_set_reset_pin(int level);
+
+/* XI(MCLK) getter -- supplied by the target.
+ *
+ * Note: when changing the supplied MCLK frequency, the NCO will need to be
+ * reprogrammed for the new master clock. Call es9218_recompute_nco() to
+ * force this. Not necessary if you're not using NCO mode.
+ */
+extern uint32_t es9218_get_mclk(void);
+
+#endif /* __ES9218_H__ */
diff --git a/firmware/target/hosted/sdl/sim-ui-defines.h b/firmware/target/hosted/sdl/sim-ui-defines.h
index 5b4030bd37..5b83c1bf12 100644
--- a/firmware/target/hosted/sdl/sim-ui-defines.h
+++ b/firmware/target/hosted/sdl/sim-ui-defines.h
@@ -529,6 +529,14 @@
#define UI_LCD_POSY 15
+#elif defined(SHANLING_Q1)
+#define UI_TITLE "Shanling Q1"
+#define UI_WIDTH 466
+#define UI_HEIGHT 526
+#define UI_LCD_POSX 46
+#define UI_LCD_POSY 61
+
+
#elif defined(SIMULATOR)
#error no UI defines
#endif
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);
#ifdef HAVE_AXP_PMU
extern bool axp_debug_menu(void);
#endif
+#ifdef HAVE_CW2015
+extern bool cw2015_debug_menu(void);
+#endif
/* Menu definition */
static const struct {
@@ -170,6 +173,9 @@ static const struct {
#ifdef HAVE_AXP_PMU
{"Power stats", &axp_debug_menu},
#endif
+#ifdef HAVE_CW2015
+ {"CW2015 debug", &cw2015_debug_menu},
+#endif
};
static 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 @@
#define DEBOUNCE_TIME (HZ/10)
static const msc_config msc_configs[] = {
-#ifdef FIIO_M3K
+#if defined(FIIO_M3K)
#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A
{
.msc_nr = 0,
@@ -52,6 +52,17 @@ static const msc_config msc_configs[] = {
.cd_gpio = GPIO_MSC0_CD,
.cd_active_level = 0,
},
+#elif defined(SHANLING_Q1)
+#define MSC_CLOCK_SOURCE X1000_CLK_MPLL
+ {
+ .msc_nr = 0,
+ .msc_type = MSC_TYPE_SD,
+ .bus_width = 4,
+ .label = "microSD",
+ .cd_gpio = GPIO_MSC0_CD,
+ .cd_active_level = 0,
+ },
+ /* NOTE: SDIO wifi card is on msc1 */
#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 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 @@
#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
const nand_chip supported_nand_chips[] = {
-#if defined(FIIO_M3K)
+#if defined(FIIO_M3K) || defined(SHANLING_Q1)
{
/* ATO25D1GA */
.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
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "audiohw.h"
+#include "system.h"
+#include "pcm_sampr.h"
+#include "aic-x1000.h"
+#include "i2c-x1000.h"
+#include "gpio-x1000.h"
+#include "x1000/aic.h"
+#include "x1000/cpm.h"
+
+/* Codec has an dedicated oscillator connected, so it can operate
+ * as i2s master or slave. I can't distinguish any difference in
+ * terms of audio quality or power consumption. Code is left here
+ * for reference in case it proves useful to change it. */
+#define CODEC_MASTER_MODE 0
+
+static int cur_fsel = HW_FREQ_48;
+static int cur_vol_l = 0, cur_vol_r = 0;
+static int cur_filter = 0;
+static enum es9218_amp_mode cur_amp_mode = ES9218_AMP_MODE_1VRMS;
+
+static void codec_start(void)
+{
+ es9218_open();
+ es9218_mute(true);
+ es9218_set_iface_role(CODEC_MASTER_MODE ? ES9218_IFACE_ROLE_MASTER
+ : ES9218_IFACE_ROLE_SLAVE);
+ es9218_set_iface_format(ES9218_IFACE_FORMAT_I2S, ES9218_IFACE_BITS_32);
+ es9218_set_dpll_bandwidth(10);
+ es9218_set_thd_compensation(true);
+ es9218_set_thd_coeffs(0, 0);
+ audiohw_set_filter_roll_off(cur_filter);
+ audiohw_set_frequency(cur_fsel);
+ audiohw_set_volume(cur_vol_l, cur_vol_r);
+ es9218_set_amp_mode(cur_amp_mode);
+}
+
+static void codec_stop(void)
+{
+ es9218_mute(true);
+ es9218_close();
+ mdelay(1);
+}
+
+void audiohw_init(void)
+{
+ /* Configure AIC */
+ aic_set_external_codec(true);
+ aic_set_i2s_mode(CODEC_MASTER_MODE ? AIC_I2S_SLAVE_MODE
+ : AIC_I2S_MASTER_MODE);
+ aic_enable_i2s_bit_clock(true);
+
+ /* Open DAC driver */
+ i2c_x1000_set_freq(1, I2C_FREQ_400K);
+ codec_start();
+}
+
+void audiohw_postinit(void)
+{
+ es9218_mute(false);
+}
+
+void audiohw_close(void)
+{
+ codec_stop();
+}
+
+void audiohw_set_frequency(int fsel)
+{
+ int sampr = hw_freq_sampr[fsel];
+
+ /* choose clock gear setting, in line with the OF */
+ enum es9218_clock_gear clkgear;
+ if(sampr <= 48000)
+ clkgear = ES9218_CLK_GEAR_4;
+ else if(sampr <= 96000)
+ clkgear = ES9218_CLK_GEAR_2;
+ else
+ clkgear = ES9218_CLK_GEAR_1;
+
+ aic_enable_i2s_bit_clock(false);
+ es9218_set_clock_gear(clkgear);
+
+ if(CODEC_MASTER_MODE)
+ es9218_set_nco_frequency(sampr);
+ else
+ aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, 64);
+
+ aic_enable_i2s_bit_clock(true);
+
+ /* save frequency selection */
+ cur_fsel = fsel;
+}
+
+static int round_step_up(int x, int step)
+{
+ int rem = x % step;
+ if(rem > 0)
+ rem -= step;
+ return x - rem;
+}
+
+void audiohw_set_volume(int vol_l, int vol_r)
+{
+ /* save volume */
+ cur_vol_l = vol_l;
+ cur_vol_r = vol_r;
+
+ /* adjust the amp setting first */
+ int amp = round_step_up(MAX(vol_l, vol_r), ES9218_AMP_VOLUME_STEP);
+ amp = MIN(amp, ES9218_AMP_VOLUME_MAX);
+ amp = MAX(amp, ES9218_AMP_VOLUME_MIN);
+
+ /* adjust digital volumes */
+ vol_l -= amp;
+ vol_l = MIN(vol_l, ES9218_DIG_VOLUME_MAX);
+ vol_l = MAX(vol_l, ES9218_DIG_VOLUME_MIN);
+
+ vol_r -= amp;
+ vol_r = MIN(vol_r, ES9218_DIG_VOLUME_MAX);
+ vol_r = MAX(vol_r, ES9218_DIG_VOLUME_MIN);
+
+ /* program DAC */
+ es9218_set_amp_volume(amp);
+ es9218_set_dig_volume(vol_l, vol_r);
+}
+
+void audiohw_set_filter_roll_off(int value)
+{
+ cur_filter = value;
+ es9218_set_filter(value);
+}
+
+void audiohw_set_power_mode(int mode)
+{
+ enum es9218_amp_mode new_amp_mode;
+ if(mode == 0)
+ new_amp_mode = ES9218_AMP_MODE_2VRMS;
+ else
+ new_amp_mode = ES9218_AMP_MODE_1VRMS;
+
+ if(new_amp_mode != cur_amp_mode) {
+ codec_stop();
+ cur_amp_mode = new_amp_mode;
+ codec_start();
+ es9218_mute(false);
+ }
+}
+
+void es9218_set_power_pin(int level)
+{
+ gpio_set_level(GPIO_ES9218_POWER, level ? 1 : 0);
+}
+
+void es9218_set_reset_pin(int level)
+{
+ gpio_set_level(GPIO_ES9218_RESET, level ? 1 : 0);
+}
+
+uint32_t es9218_get_mclk(void)
+{
+ /* Measured by running the DAC in asynchronous I2S slave mode,
+ * and reading back the DPLL number from regs 0x42-0x45 while
+ * playing back 44.1 KHz audio.
+ *
+ * CLK = (44_100 * 2**32) / 0x4b46e5
+ * = 38_393_403.29532737
+ * ~ 38.4 Mhz
+ */
+ return 38400000;
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "backlight.h"
+#include "backlight-target.h"
+#include "lcd.h"
+#include "pwm-x1000.h"
+
+#define BL_LCD_CHN 0
+#define BL_LCD_PERIOD 10000
+
+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/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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 __BACKLIGHT_TARGET_H__
+#define __BACKLIGHT_TARGET_H__
+
+#include
+
+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/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 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+include $(ROOTDIR)/lib/microtar/microtar.make
+
+.SECONDEXPANSION:
+
+# FIXME(q1): verify NAND parameters
+$(BUILDDIR)/spl.q1: $(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.q1 \
+ $(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/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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ * Copyright (C) 2021 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 "touchscreen.h"
+#include "ft6x06.h"
+#include "axp-pmu.h"
+#include "kernel.h"
+#include "backlight.h"
+#include "powermgmt.h"
+#include "gpio-x1000.h"
+#include "irq-x1000.h"
+#include "i2c-x1000.h"
+#include
+
+/* Volume wheel rotation */
+static volatile int wheel_pos = 0;
+
+/* Value of headphone detect register */
+static uint8_t hp_detect_reg = 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)
+{
+ /* TODO: replace this copy paste cruft with an API in axp-pmu */
+ 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 detect is wired to AXP192 GPIO: set it to input state */
+ i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01);
+
+ /* 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;
+
+ /* Poll the register every second */
+ timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc);
+}
+
+void button_init_device(void)
+{
+ /* Setup interrupts for the volume wheel */
+ gpio_set_function(GPIO_WHEEL1, GPIOF_IRQ_EDGE(0));
+ gpio_set_function(GPIO_WHEEL2, GPIOF_IRQ_EDGE(0));
+ gpio_flip_edge_irq(GPIO_WHEEL1);
+ gpio_flip_edge_irq(GPIO_WHEEL2);
+ gpio_enable_irq(GPIO_WHEEL1);
+ gpio_enable_irq(GPIO_WHEEL2);
+
+ /* Init touchscreen driver */
+ i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K);
+ ft6x06_init();
+
+ /* Reset touch controller */
+ gpio_set_level(GPIO_FT6x06_POWER, 1);
+ gpio_set_level(GPIO_FT6x06_RESET, 0);
+ mdelay(5);
+ gpio_set_level(GPIO_FT6x06_RESET, 1);
+
+ /* Enable ft6x06 interrupt */
+ system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT), ft6x06_irq_handler);
+ gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0));
+ gpio_enable_irq(GPIO_FT6x06_INTERRUPT);
+
+ /* Headphone detection */
+ hp_detect_init();
+}
+
+int button_read_device(int* data)
+{
+ int r = 0;
+
+ /* Read GPIO buttons, these are all active low */
+ uint32_t b = REG_GPIO_PIN(GPIO_B);
+ if((b & (1 << 21)) == 0) r |= BUTTON_PREV;
+ if((b & (1 << 22)) == 0) r |= BUTTON_NEXT;
+ if((b & (1 << 28)) == 0) r |= BUTTON_PLAY;
+ if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
+
+ /* Check the wheel */
+ int wheel_btn = 0;
+ int whpos = wheel_pos;
+ if(whpos > 3)
+ wheel_btn = BUTTON_VOL_DOWN;
+ else if(whpos < -3)
+ wheel_btn = BUTTON_VOL_UP;
+
+ if(wheel_btn) {
+ wheel_pos = 0;
+
+ /* Post the event (rapid motion is more reliable this way) */
+ queue_post(&button_queue, wheel_btn, 0);
+ queue_post(&button_queue, wheel_btn|BUTTON_REL, 0);
+
+ /* Poke the backlight */
+ backlight_on();
+ reset_poweroff_timer();
+ }
+
+ /* Handle touchscreen
+ *
+ * TODO: Support 2-point multitouch (useful for 3x3 grid mode)
+ * TODO: Support simple gestures by converting them to fake buttons
+ */
+ int t = touchscreen_to_pixels(ft6x06_state.pos_x, ft6x06_state.pos_y, data);
+ if(ft6x06_state.event == FT6x06_EVT_PRESS ||
+ ft6x06_state.event == FT6x06_EVT_CONTACT) {
+ /* Only set the button bit if the screen is being touched. */
+ r |= t;
+ }
+
+ return r;
+}
+
+void touchscreen_enable_device(bool en)
+{
+ ft6x06_enable(en);
+ /* TODO: check if it's worth shutting off the power pin */
+}
+
+bool headphones_inserted(void)
+{
+ /* TODO: Also check if the headset button is detectable via an ADC.
+ * The AXP driver should probably get proper interrupt handling,
+ * that would be useful for more things than just GPIO polling. */
+ return hp_detect_reg & 0x20 ? true : false;
+}
+
+static void handle_wheel_irq(void)
+{
+ /* Wheel stuff adapted from button-erosqnative.c */
+ static const int delta[16] = { 0, -1, 1, 0,
+ 1, 0, 0, -1,
+ -1, 0, 0, 1,
+ 0, 1, -1, 0 };
+ static uint32_t state = 0;
+ state <<= 2;
+ state |= (REG_GPIO_PIN(GPIO_D) >> 2) & 3;
+ state &= 0xf;
+
+ wheel_pos += delta[state];
+}
+
+void GPIOD02(void)
+{
+ handle_wheel_irq();
+ gpio_flip_edge_irq(GPIO_WHEEL1);
+}
+
+void GPIOD03(void)
+{
+ handle_wheel_irq();
+ gpio_flip_edge_irq(GPIO_WHEEL2);
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 __BUTTON_TARGET_H__
+#define __BUTTON_TARGET_H__
+
+#include
+
+/* physical buttons */
+#define BUTTON_POWER 0x00000001
+#define BUTTON_VOL_UP 0x00000002 /* up = wheel clockwise */
+#define BUTTON_VOL_DOWN 0x00000004
+#define BUTTON_PLAY 0x00000008 /* circle */
+#define BUTTON_NEXT 0x00000010 /* down */
+#define BUTTON_PREV 0x00000020 /* up */
+
+/* compatibility hacks */
+#define BUTTON_LEFT BUTTON_MIDLEFT
+#define BUTTON_RIGHT BUTTON_MIDRIGHT
+
+/* touchscreen "buttons" */
+#define BUTTON_TOPLEFT 0x00000040
+#define BUTTON_TOPMIDDLE 0x00000080
+#define BUTTON_TOPRIGHT 0x00000100
+#define BUTTON_MIDLEFT 0x00000200
+#define BUTTON_CENTER 0x00000400
+#define BUTTON_MIDRIGHT 0x00000800
+#define BUTTON_BOTTOMLEFT 0x00001000
+#define BUTTON_BOTTOMMIDDLE 0x00002000
+#define BUTTON_BOTTOMRIGHT 0x00004000
+
+#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\
+ BUTTON_PLAY|BUTTON_NEXT|BUTTON_PREV)
+
+#define POWEROFF_BUTTON BUTTON_POWER
+#define POWEROFF_COUNT 30
+
+#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 @@
+/* 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(I2C0, GPIO_B, 3 << 23, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0))
+DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1))
+
+/* Name Pin Function */
+DEFINE_GPIO(FT6x06_INTERRUPT, GPIO_PA(16), GPIOF_INPUT)
+DEFINE_GPIO(USB_DETECT, GPIO_PA(17), GPIOF_INPUT)
+DEFINE_GPIO(FT6x06_RESET, GPIO_PA(19), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_PWR, GPIO_PB(6), GPIOF_OUTPUT(1))
+DEFINE_GPIO(FT6x06_POWER, GPIO_PB(8), GPIOF_OUTPUT(0))
+DEFINE_GPIO(MSC0_CD, GPIO_PB(9), GPIOF_INPUT)
+DEFINE_GPIO(ES9218_POWER, GPIO_PB(13), GPIOF_OUTPUT(0))
+DEFINE_GPIO(LCD_RST, GPIO_PB(15), GPIOF_OUTPUT(1))
+DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1))
+DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1))
+DEFINE_GPIO(BTN_PREV, GPIO_PB(21), GPIOF_INPUT)
+DEFINE_GPIO(BTN_NEXT, GPIO_PB(22), GPIOF_INPUT)
+DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0))
+DEFINE_GPIO(BTN_PLAY, GPIO_PB(28), GPIOF_INPUT)
+DEFINE_GPIO(BTN_POWER, GPIO_PB(31), GPIOF_INPUT)
+DEFINE_GPIO(AXP_IRQ, GPIO_PC(21), GPIOF_INPUT)
+DEFINE_GPIO(USB_ID, GPIO_PC(23), GPIOF_INPUT)
+DEFINE_GPIO(WHEEL1, GPIO_PD(2), GPIOF_INPUT)
+DEFINE_GPIO(WHEEL2, GPIO_PD(3), GPIOF_INPUT)
+DEFINE_GPIO(ES9218_GPIO2, GPIO_PD(4), GPIOF_OUTPUT(0))
+DEFINE_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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 __I2C_TARGET_H__
+#define __I2C_TARGET_H__
+
+#define I2C_ASYNC_BUS_COUNT 3
+#define I2C_ASYNC_QUEUE_SIZE 4
+
+#define FT6x06_BUS 0
+#define FT6x06_ADDR 0x38
+
+#define ES9218_BUS 1
+#define ES9218_ADDR 0x48
+
+#define AXP_PMU_BUS 2
+#define AXP_PMU_ADDR 0x34
+
+#define CW2015_BUS 2
+#define CW2015_ADDR 0x62
+
+#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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "lcd.h"
+#include "system.h"
+#include "lcd-x1000.h"
+#include "gpio-x1000.h"
+
+/* LCD controller is probably an RM68090.
+ */
+
+static const uint32_t q1_lcd_cmd_enable[] = {
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0xbe,
+ LCD_INSTR_DAT, 0xc3,
+ LCD_INSTR_DAT, 0x29,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x10,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x05,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x08,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x0d,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0xc1,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_DAT, 0xb1,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x12,
+ LCD_INSTR_DAT, 0xb1,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x13,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x0f,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x14,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x14,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x15,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x16,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x22,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x23,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x30,
+ LCD_INSTR_DAT, 0x7c,
+ LCD_INSTR_DAT, 0x3f,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x32,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x70,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x91,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0xe0,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0xe1,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x61,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x10,
+ LCD_INSTR_DAT, 0x30,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0xf6,
+ LCD_INSTR_DAT, 0x3f,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_DAT, 0x50,
+ LCD_INSTR_DAT, 0x1f,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x30,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x08,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x35,
+ LCD_INSTR_DAT, 0x76,
+ LCD_INSTR_DAT, 0x66,
+
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_CMD, 0x39,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x26,
+
+ LCD_INSTR_CMD, 0x04,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0xc7,
+
+ LCD_INSTR_CMD, 0x04,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x0d,
+ LCD_INSTR_DAT, 0x0e,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_DAT, 0x08,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x02,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x04,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x05,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x06,
+ LCD_INSTR_DAT, 0x1b,
+ LCD_INSTR_DAT, 0x21,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x0f,
+ LCD_INSTR_DAT, 0x0e,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x08,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x04,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x09,
+ LCD_INSTR_DAT, 0x08,
+ LCD_INSTR_DAT, 0x08,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0a,
+ LCD_INSTR_DAT, 0x02,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0b,
+ LCD_INSTR_DAT, 0x03,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0c,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_CMD, 0x0d,
+ LCD_INSTR_DAT, 0x31,
+ LCD_INSTR_DAT, 0x34,
+
+ /* X start */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x1e, /* 30 */
+
+ /* X end */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x11,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x85, /* 389 */
+
+ /* Y start */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x12,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00, /* 0 */
+
+ /* Y end */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x13,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x8f, /* 399 */
+
+ /* RAM write start X? */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x1e,
+
+ /* RAM write start Y? */
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x01,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x00,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x03,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x30,
+
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_CMD, 0x02,
+ LCD_INSTR_END,
+};
+
+/* NOTE this sleep mode may not be saving power, but it gets rid of the
+ * ghost image that would otherwise remain on the display */
+static const uint32_t q1_lcd_cmd_sleep[] = {
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x01,
+
+ LCD_INSTR_END,
+};
+
+static const uint32_t q1_lcd_cmd_wake[] = {
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x07,
+ LCD_INSTR_DAT, 0x01,
+ LCD_INSTR_DAT, 0x03,
+
+ LCD_INSTR_CMD, 0x00,
+ LCD_INSTR_CMD, 0x10,
+ LCD_INSTR_DAT, 0x00,
+ LCD_INSTR_DAT, 0xc1,
+
+ LCD_INSTR_END,
+};
+
+static const uint8_t __attribute__((aligned(64)))
+ q1_lcd_dma_wr_cmd[] = {0x02, 0x02, 0x02, 0x02};
+
+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,
+ .big_endian = 1,
+ .dma_wr_cmd_buf = &q1_lcd_dma_wr_cmd,
+ .dma_wr_cmd_size = sizeof(q1_lcd_dma_wr_cmd),
+};
+
+void lcd_tgt_enable(bool enable)
+{
+ if(enable) {
+ /* power on the panel */
+ gpio_set_level(GPIO_LCD_PWR, 1);
+ gpio_set_level(GPIO_LCD_RST, 1);
+ gpio_set_level(GPIO_LCD_CE, 1);
+ gpio_set_level(GPIO_LCD_RD, 1);
+ mdelay(50);
+ gpio_set_level(GPIO_LCD_RST, 0);
+ mdelay(100);
+ gpio_set_level(GPIO_LCD_RST, 1);
+ mdelay(50);
+ gpio_set_level(GPIO_LCD_CE, 0);
+
+ /* Start the controller */
+ lcd_set_clock(X1000_CLK_MPLL, 50000000);
+ lcd_exec_commands(q1_lcd_cmd_enable);
+ } else {
+ /* FIXME: Shanling Q1 LCD power down sequence
+ * not important because we don't use it but it'd be nice to know */
+ }
+}
+
+void lcd_tgt_sleep(bool sleep)
+{
+ if(sleep)
+ lcd_exec_commands(q1_lcd_cmd_sleep);
+ else
+ lcd_exec_commands(q1_lcd_cmd_wake);
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "power.h"
+#include "adc.h"
+#include "system.h"
+#include "axp-pmu.h"
+#ifdef HAVE_CW2015
+# include "cw2015.h"
+#endif
+#ifdef HAVE_USB_CHARGING_ENABLE
+# include "usb_core.h"
+#endif
+
+#include "i2c-x1000.h"
+
+/* TODO: Better(?) battery reporting for Q1 using CW2015 driver
+ *
+ * The CW2015 has its own quirks so the driver has to be more complicated
+ * than "read stuff from I2C," unfortunately. Without fixing the quirks it
+ * is probably worse than the simple voltage-based method.
+ *
+ * A bigger problem is that it shares an I2C bus with the AXP192, but when
+ * we attempt to communicate with both chips, they start returning bogus
+ * data intermittently. Ususally, reads will return 0 but sometimes they
+ * can return other nonzero bogus data. It could be that one or the other is
+ * pulling the bus line down inappropriately, or maybe the hardware does not
+ * respect the bus free time between start/stop conditions and one of the
+ * devices is getting confused.
+ */
+
+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)
+{
+ i2c_x1000_set_freq(AXP_PMU_BUS, I2C_FREQ_400K);
+ axp_init();
+#ifdef HAVE_CW2015
+ cw2015_init();
+#endif
+
+ /* Change supply voltage from the default of 1250 mV to 1200 mV,
+ * this matches the original firmware's settings. Didn't observe
+ * any obviously bad behavior at 1250 mV, but better to be safe. */
+ axp_supply_set_voltage(AXP_SUPPLY_DCDC2, 1200);
+
+ /* For now, just turn everything on... definitely the touchscreen
+ * is powered by one of the outputs */
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL1, 0, 0x05, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_PWROUTPUTCTRL2, 0, 0x0f, NULL);
+ i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR,
+ AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL);
+
+ /* Delay to give power output time to stabilize */
+ mdelay(20);
+}
+
+#ifdef HAVE_USB_CHARGING_ENABLE
+void usb_charging_maxcurrent_change(int maxcurrent)
+{
+ axp_set_charge_current(maxcurrent);
+}
+#endif
+
+void power_off(void)
+{
+ axp_power_off();
+ while(1);
+}
+
+bool charging_state(void)
+{
+ return axp_battery_status() == AXP_BATT_CHARGING;
+}
+
+int _battery_voltage(void)
+{
+ /* CW2015 can also read battery voltage, but the AXP consistently
+ * reads ~20-30 mV higher so I suspect it's the "real" voltage. */
+ return axp_adc_read(ADC_BATTERY_VOLTAGE);
+}
+
+#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & PERCENTAGE_MEASURE) != 0
+int _battery_level(void)
+{
+ return cw2015_get_soc();
+}
+#endif
+
+#if defined(HAVE_CW2015) && (CONFIG_BATTERY_MEASURE & TIME_MEASURE) != 0
+int _battery_time(void)
+{
+ return cw2015_get_rrt();
+}
+#endif
+
+void adc_init(void)
+{
+}
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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"
+
+#define CMDLINE_COMMON \
+ "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
+#define CMDLINE_NORMAL \
+ " init=/linuxrc ubi.mtd=5 root=ubi0:rootfs ubi.mtd=6 rootfstype=ubifs rw"
+
+static int dualboot_setup(void)
+{
+ spl_dualboot_init_clocktree();
+ spl_dualboot_init_uart2();
+
+ /* load PDMA MCU firmware */
+ jz_writef(CPM_CLKGR, PDMA(0));
+ return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
+}
+
+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,
+ },
+ [BOOT_OPTION_OFW_PLAYER] = {
+ .storage_addr = 0x140000,
+ .storage_size = 8 * 1024 * 1024,
+ .load_addr = 0x80efffc0,
+ .exec_addr = 0x80f00000,
+ .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
+ .cmdline_addr = 0x80004000,
+ .setup = dualboot_setup,
+ },
+ [BOOT_OPTION_OFW_RECOVERY] = {
+ .storage_addr = 0x940000,
+ .storage_size = 10 * 1024 * 1024,
+ .load_addr = 0x80efffc0,
+ .exec_addr = 0x80f00000,
+ .cmdline = CMDLINE_COMMON,
+ .cmdline_addr = 0x80004000,
+ .setup = dualboot_setup,
+ },
+};
+
+int spl_get_boot_option(void)
+{
+ /* Button debounce time in OST clock cycles */
+ const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
+
+ /* Buttons to poll */
+ const unsigned port = GPIO_B;
+ const uint32_t recov_pin = (1 << 22); /* Next */
+ const uint32_t orig_fw_pin = (1 << 21); /* Prev */
+
+ uint32_t pin = -1, lastpin = 0;
+ uint32_t deadline = 0;
+ int iter_count = 30; /* to avoid an infinite loop */
+
+ /* set GPIOs to input state */
+ gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
+
+ /* Poll until we get a stable reading */
+ do {
+ lastpin = pin;
+ pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
+ if(pin != lastpin) {
+ deadline = __ost_read32() + btn_stable_time;
+ iter_count -= 1;
+ }
+ } while(iter_count > 0 && __ost_read32() < deadline);
+
+ if(iter_count >= 0 && (pin & orig_fw_pin)) {
+ if(pin & recov_pin)
+ return BOOT_OPTION_OFW_RECOVERY;
+ else
+ return BOOT_OPTION_OFW_PLAYER;
+ }
+
+ return BOOT_OPTION_ROCKBOX;
+}
+
+void spl_error(void)
+{
+ /* Flash the backlight */
+ int level = 0;
+ while(1) {
+ gpio_set_function(GPIO_PC(25), GPIOF_OUTPUT(level));
+ mdelay(100);
+ level = 1 - level;
+ }
+}
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 @@
#include "ucl_decompress.h"
#include
-#ifdef FIIO_M3K
+#if defined(FIIO_M3K) || defined(SHANLING_Q1)
# define SPL_DDR_MEMORYSIZE 64
# define SPL_DDR_AUTOSR_EN 1
# define SPL_DDR_NEED_BYPASS 1
diff --git a/tools/configure b/tools/configure
index 2e06b03717..f32c5d514d 100755
--- a/tools/configure
+++ b/tools/configure
@@ -1597,7 +1597,8 @@ cat < ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 by 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
+#include "button.h"
+#include "buttonmap.h"
+
+int key_to_button(int keyboard_button)
+{
+ int new_btn = BUTTON_NONE;
+ switch (keyboard_button)
+ {
+ case SDLK_KP8:
+ case SDLK_UP:
+ new_btn = BUTTON_PREV;
+ break;
+
+ case SDLK_KP2:
+ case SDLK_DOWN:
+ new_btn = BUTTON_NEXT;
+ break;
+
+ case SDLK_KP5:
+ case SDLK_RETURN:
+ case SDLK_SPACE:
+ new_btn = BUTTON_PLAY;
+ break;
+
+ case SDLK_ESCAPE:
+ new_btn = BUTTON_POWER;
+ break;
+
+ case SDLK_KP_PLUS:
+ case SDLK_EQUALS:
+ new_btn = BUTTON_VOL_UP;
+ break;
+
+ case SDLK_KP_MINUS:
+ case SDLK_MINUS:
+ new_btn = BUTTON_VOL_DOWN;
+ break;
+ }
+ return new_btn;
+}
+
+struct button_map bm[] = {
+ { SDLK_KP_PLUS, 426, 140, 40, "Volume +" },
+ { SDLK_KP_MINUS, 426, 180, 40, "Volume -" },
+ { SDLK_SPACE, 0, 244, 40, "Play" },
+ { SDLK_UP, 0, 133, 40, "Previous" },
+ { SDLK_DOWN, 0, 357, 40, "Next" },
+ { 0, 0, 0, 0, "None" }
+};
diff --git a/wps/AUTHORS b/wps/AUTHORS
index 4f0d852286..0afa5b9896 100644
--- a/wps/AUTHORS
+++ b/wps/AUTHORS
@@ -16,6 +16,7 @@ Cabbie v2.0:
Johannes Voggenthaler
Jonathan Gordon
Keith Perri
+ Marc Aarts
Marc Guay
Marcin Bukat
Marianne Arnold
diff --git a/wps/WPSLIST b/wps/WPSLIST
index 70970dee58..efb1030585 100644
--- a/wps/WPSLIST
+++ b/wps/WPSLIST
@@ -82,6 +82,7 @@ RSBS: no
wps.800x480x(16|24): cabbiev2.800x480x16.wps
wps.480x800x(16|24): cabbiev2.480x800x16.wps
wps.400x240x(16|24): cabbiev2.400x240x16.wps
+wps.360x400x16: cabbiev2.360x400x16.wps
wps.320x480x(16|24): cabbiev2.320x480x16.wps
wps.320x240x(16|24|32): cabbiev2.320x240x16.wps
wps.240x400x(16|24): cabbiev2.240x400x16.wps
@@ -111,6 +112,7 @@ fms.128x128x2: cabbiev2-128x128x2.fms
Font.800x480x(16|24): 35-Adobe-Helvetica.fnt
Font.480x800x(16|24): 35-Adobe-Helvetica.fnt
Font.400x240x(16|24): 15-Adobe-Helvetica.fnt
+Font.360x400x16: 18-Adobe-Helvetica.fnt
Font.320x480x(16|24): 27-Adobe-Helvetica.fnt
Font.320x240x(16|24): 15-Adobe-Helvetica.fnt
Font.240x400x(16|24): 16-Adobe-Helvetica.fnt
@@ -144,6 +146,7 @@ filetype colours: -
backdrop.800x480x(16|24): backdrops/cabbiev2.800x480x16.bmp
backdrop.480x800x(16|24): backdrops/cabbiev2.480x800x16.bmp
backdrop.400x240x(16|24): backdrops/cabbiev2.400x240x16.bmp
+backdrop.360x400x16: backdrops/cabbiev2.360x400x16.bmp
backdrop.320x480x(16|24): backdrops/cabbiev2.320x480x16.bmp
backdrop.320x240x(16|24): backdrops/cabbiev2.320x240x16.bmp
backdrop.128x128x(16|24): backdrops/cabbiev2.128x128x16.bmp
@@ -170,6 +173,7 @@ selector type..+x2: bar (inverse)
iconset.800x480x(16|24): icons/tango_icons.32x32.bmp
iconset.480x800x(16|24): icons/tango_icons.32x32.bmp
iconset.400x240x(16|24): icons/tango_icons.16x16.bmp
+iconset.360x400x16: icons/tango_icons.32x32.bmp
iconset.320x480x(16|24): icons/tango_icons.24x24.bmp
iconset.320x240x(16|24|32): icons/tango_icons.16x16.bmp
iconset.128x128x(16|24): icons/tango_icons.12x12.bmp
@@ -189,6 +193,7 @@ iconset..+x2: icons/tango_small_mono.bmp
viewers iconset.800x480x(16|24): icons/tango_icons_viewers.32x32.bmp
viewers iconset.480x800x(16|24): icons/tango_icons_viewers.32x32.bmp
viewers iconset.400x240x(16|24): icons/tango_icons_viewers.16x16.bmp
+viewers iconset.360x400x16: icons/tango_icons_viewers.32x32.bmp
viewers iconset.320x480x(16|24): icons/tango_icons_viewers.24x24.bmp
viewers iconset.320x240x(16|24): icons/tango_icons_viewers.16x16.bmp
viewers iconset.128x128x(16|24): icons/tango_icons_viewers.12x12.bmp
diff --git a/wps/cabbiev2.360x400x16.wps b/wps/cabbiev2.360x400x16.wps
new file mode 100644
index 0000000000..163d749e50
--- /dev/null
+++ b/wps/cabbiev2.360x400x16.wps
@@ -0,0 +1,81 @@
+# Cabbie v2.0
+# (C) 2007-2012 The Authors (see /rockbox/wps/AUTHORS)
+# Derived from "cabbie" (C) Yohann Misquitta
+#
+# Disable Status Bar
+%wd
+#
+# Load Backdrop
+%X(wpsbackdrop-360x400x16.bmp)
+#
+# Preload Images
+%xl(A,lock-360x400x16.bmp,0,0,2)
+%xl(B,battery-360x400x16.bmp,0,0,10)
+%xl(C,volume-360x400x16.bmp,0,0,10)
+%xl(D,shuffle-360x400x16.bmp,0,0)
+%xl(E,repeat-360x400x16.bmp,0,0,4)
+%xl(F,playmode-360x400x16.bmp,0,0,5)
+#
+# Album Art/Info Viewport Conditional
+%?C<%Vd(a)|%Vd(b)>
+#
+# Progress Bar
+%V(33,316,300,20,-)
+%pb(0,0,-,20,pb-360x400x16.bmp)
+#
+# Hold
+%V(16,365,38,29,-)
+%?mh<%xd(Aa)|%xd(Ab)>
+#
+# Battery
+%V(69,365,66,29,-)
+%?bp<%?bc<%xd(Ba)|%xd(Bb)>|%?bl<|%xd(Bc)|%xd(Bd)|%xd(Be)|%xd(Bf)|%xd(Bg)|%xd(Bh)|%xd(Bi)|%xd(Bj)>>
+#
+# Volume
+%V(147,365,50,29,-)
+%?pv<%xd(Ca)|%xd(Cb)|%xd(Cc)|%xd(Cd)|%xd(Ce)|%xd(Cf)|%xd(Cg)|%xd(Ch)|%xd(Ci)|%xd(Cj)>
+#
+# Shuffle
+%V(208,365,55,29,-)
+%?ps<%xd(D)>
+#
+# Repeat
+%V(273,365,27,29,-)
+%?mm<|%xd(Ea)|%xd(Eb)|%xd(Ec)|%xd(Ed)>
+#
+# Playmode
+%V(309,365,36,29,-)
+%?mp<%xd(Fa)|%xd(Fb)|%xd(Fc)|%xd(Fd)|%xd(Fe)>
+#
+# Time Elapsed/Remaining
+%V(32,339,300,20,-)
+%al%pc%ac%?Sr<%pe %Sx(of) %pp|%pp %Sx(of) %pe>%ar%pr
+#
+# Album Art
+%ax%Vl(a,82,38,195,161,-)
+%Cl(0,0,195,161,c,c)
+%Cd
+#
+# Track Info - Album Art
+%ax%Vl(a,0,206,-,98,1)
+%s%ac%?it<%it|%fn>
+%s%ac%?ia<%ia|%?iA<%iA|%?d(2)<%d(2)|%(root%)>>>
+%s%ac%?id<%id|%?d(1)<%d(1)|%(root%)>>
+
+%s%ac%Sx(Next:) %?Ia<%Ia|%?IA<%IA|%?D(2)<%D(2)|%(root%)>>> - %?It<%It|%Fn>
+#
+# Track Info - No Album Art
+%Vl(b,0,56,-,247,1)
+%s%ac%?it<%it|%fn>
+%s%ac%?ia<%ia|%?iA<%iA|%?d(2)<%d(2)|%(root%)>>>
+%s%ac%?id<%id|%?d(1)<%d(1)|%(root%)>>
+%ac%?iy<%iy|>
+
+%ac%?ig<%ig|>
+%ac%?fv<%(vbr%) |>%fb kbit/s %fc
+
+
+%ac%Sx(Next Track:)
+%ac%s%?It<%It|%Fn>
+%s%ac%?Ia<%Ia|%?IA<%IA|%?D(2)<%D(2)|%(root%)>>>
+%s%ac%?Id<%Id|%?D(1)<%D(1)|%(root%)>>
diff --git a/wps/cabbiev2/battery-360x400x16.bmp b/wps/cabbiev2/battery-360x400x16.bmp
new file mode 100644
index 0000000000..d00b4c1c5d
Binary files /dev/null and b/wps/cabbiev2/battery-360x400x16.bmp differ
diff --git a/wps/cabbiev2/lock-360x400x16.bmp b/wps/cabbiev2/lock-360x400x16.bmp
new file mode 100644
index 0000000000..70c419e875
Binary files /dev/null and b/wps/cabbiev2/lock-360x400x16.bmp differ
diff --git a/wps/cabbiev2/pb-360x400x16.bmp b/wps/cabbiev2/pb-360x400x16.bmp
new file mode 100644
index 0000000000..6b5884d059
Binary files /dev/null and b/wps/cabbiev2/pb-360x400x16.bmp differ
diff --git a/wps/cabbiev2/playmode-360x400x16.bmp b/wps/cabbiev2/playmode-360x400x16.bmp
new file mode 100644
index 0000000000..148376b127
Binary files /dev/null and b/wps/cabbiev2/playmode-360x400x16.bmp differ
diff --git a/wps/cabbiev2/repeat-360x400x16.bmp b/wps/cabbiev2/repeat-360x400x16.bmp
new file mode 100644
index 0000000000..f6fb08d909
Binary files /dev/null and b/wps/cabbiev2/repeat-360x400x16.bmp differ
diff --git a/wps/cabbiev2/shuffle-360x400x16.bmp b/wps/cabbiev2/shuffle-360x400x16.bmp
new file mode 100644
index 0000000000..1b16153176
Binary files /dev/null and b/wps/cabbiev2/shuffle-360x400x16.bmp differ
diff --git a/wps/cabbiev2/volume-360x400x16.bmp b/wps/cabbiev2/volume-360x400x16.bmp
new file mode 100644
index 0000000000..7d8b8e40e8
Binary files /dev/null and b/wps/cabbiev2/volume-360x400x16.bmp differ
diff --git a/wps/cabbiev2/wpsbackdrop-360x400x16.bmp b/wps/cabbiev2/wpsbackdrop-360x400x16.bmp
new file mode 100644
index 0000000000..aa7399bd53
Binary files /dev/null and b/wps/cabbiev2/wpsbackdrop-360x400x16.bmp differ