New port: FiiO M3K on bare metal

Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
This commit is contained in:
Aidan MacDonald 2021-02-27 22:08:58 +00:00
parent 83fcbedc65
commit 3ec66893e3
143 changed files with 16585 additions and 24 deletions

1
.gitignore vendored
View file

@ -118,6 +118,7 @@ __pycache__
/tools/mknkboot
/tools/mktccboot
/tools/mkzenboot
/tools/mkspl-x1000
/tools/iaudio_bl_flash.c
/tools/iaudio_bl_flash.h
/tools/.vagrant

View file

@ -302,6 +302,8 @@ keymaps/keymap-xduoox3ii.c
keymaps/keymap-xduoox20.c
#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
keymaps/keymap-fiiom3klinux.c
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
keymaps/keymap-fiiom3k.c
#elif CONFIG_KEYPAD == EROSQ_PAD
keymaps/keymap-erosq.c
#endif

View file

@ -126,6 +126,10 @@
#include "bootdata.h"
#endif
#ifdef FIIO_M3K
#include "installer.h"
#endif
static const char* threads_getname(int selected_item, void *data,
char *buffer, size_t buffer_len)
{
@ -2491,6 +2495,52 @@ static bool dbg_boot_data(void)
}
#endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */
#ifdef FIIO_M3K
/* Note: this is temporary and should NOT be merged, ensure it is removed */
static int fiio_debug_menu_action_callback(int action, struct gui_synclist *lists)
{
if(action == ACTION_REDRAW) {
simplelist_set_line_count(0);
simplelist_addline("Back to menu");
simplelist_addline("Install bootloader");
simplelist_addline("Dump bootloader");
action = ACTION_REDRAW;
}
if(action == ACTION_STD_OK) {
int sel = gui_synclist_get_sel_pos(lists);
int rc = 0;
switch(sel) {
case 1:
rc = install_bootloader("/boot.install");
break;
case 2:
rc = dump_bootloader("/boot.dump");
break;
default:
break;
}
if(sel == 1 || sel == 2) {
const char* msg = installer_strerror(rc);
splashf(3*HZ, "(%d) %s", rc, msg);
}
action = ACTION_STD_CANCEL;
}
return action;
}
static bool dbg_fiio_menu(void)
{
struct simplelist_info info;
simplelist_info_init(&info, "FiiO debug menu", 3, NULL);
info.action_callback = fiio_debug_menu_action_callback;
return simplelist_show_list(&info);
}
#endif
/****** The menu *********/
static const struct {
unsigned char *desc; /* string or ID */
@ -2598,6 +2648,9 @@ static const struct {
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
{"Boot data", dbg_boot_data },
#endif
#ifdef FIIO_M3K
{"FiiO debug menu", dbg_fiio_menu},
#endif
};
static int menu_action_callback(int btn, struct gui_synclist *lists)

View file

@ -182,10 +182,15 @@ 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)
#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)
filter_roll_off
#endif
/* This should be AUDIOHW_HAVE_POWER_MODE but that is not defined yet */
#if defined(HAVE_AK4376)
dac_power_mode
#endif
#if defined(HAVE_ES9018)
es9018
#endif

View file

@ -25,7 +25,7 @@
#include "screen_access.h"
#include "settings.h"
#if defined (HAVE_SCROLLWHEEL)
#if defined (HAVE_SCROLLWHEEL) && !defined(FIIO_M3K)
/* Define this if your target makes sense to have
smaller values at the top of the list increasing down the list */
#define ASCENDING_INT_SETTINGS

View file

@ -0,0 +1,219 @@
/***************************************************************************
* __________ __ ___.
* 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 FiiO M3K 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_UP, BUTTON_NONE},
{ACTION_STD_PREVREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_PREV, BUTTON_SCROLL_BACK, BUTTON_NONE},
{ACTION_STD_PREVREPEAT, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_NEXT, BUTTON_SCROLL_FWD, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_OK, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
{ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
{ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
{ACTION_STD_MENU, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
{ACTION_STD_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
{ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
{ACTION_STD_HOTKEY, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
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_PLAY, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_WPS_STOP, BUTTON_POWER|BUTTON_REPEAT, BUTTON_POWER},
{ACTION_WPS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_WPS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_WPS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_SKIPNEXT, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT},
{ACTION_WPS_SKIPPREV, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT},
{ACTION_WPS_SEEKFWD, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_STOPSEEK, BUTTON_RIGHT|BUTTON_REL, BUTTON_RIGHT|BUTTON_REPEAT},
{ACTION_WPS_SEEKBACK, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_WPS_STOPSEEK, BUTTON_LEFT|BUTTON_REL, BUTTON_LEFT|BUTTON_REPEAT},
{ACTION_WPS_BROWSE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
{ACTION_WPS_MENU, BUTTON_BACK|BUTTON_REL, BUTTON_BACK},
{ACTION_WPS_CONTEXT, BUTTON_MENU|BUTTON_REL, BUTTON_MENU},
{ACTION_WPS_QUICKSCREEN, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
{ACTION_STD_KEYLOCK, BUTTON_POWER|BUTTON_REL, BUTTON_POWER},
{ACTION_WPS_HOTKEY, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
{ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_FWD, BUTTON_NONE},
{ACTION_WPS_VIEW_PLAYLIST, BUTTON_SCROLL_BACK, BUTTON_NONE},
{ACTION_WPS_ABSETA_PREVDIR, BUTTON_UP|BUTTON_REPEAT, BUTTON_UP},
{ACTION_WPS_ABSETB_NEXTDIR, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_DOWN},
{ACTION_WPS_ABRESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
LAST_ITEM_IN_LIST
}; /* button_context_wps */
static const struct button_mapping button_context_tree[] = {
{ACTION_TREE_STOP, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY},
{ACTION_TREE_WPS, BUTTON_BACK|BUTTON_REPEAT, BUTTON_BACK},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST)
}; /* button_context_tree */
static const struct button_mapping button_context_list[] = {
{ACTION_LISTTREE_PGUP, BUTTON_LEFT, BUTTON_NONE},
{ACTION_LISTTREE_PGUP, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_LISTTREE_PGDOWN, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_LISTTREE_PGDOWN, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_LIST_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_LIST_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_LIST_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_list */
static const struct button_mapping button_context_settings[] = {
{ACTION_SETTINGS_INC, BUTTON_UP, BUTTON_NONE},
{ACTION_SETTINGS_INCREPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_INC, BUTTON_SCROLL_BACK, BUTTON_NONE},
{ACTION_SETTINGS_INCREPEAT, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_SETTINGS_DEC, BUTTON_DOWN, BUTTON_NONE},
{ACTION_SETTINGS_DECREPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_DEC, BUTTON_SCROLL_FWD, BUTTON_NONE},
{ACTION_SETTINGS_DECREPEAT, BUTTON_SCROLL_FWD|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_SETTINGS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
{ACTION_STD_NEXT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_STD_NEXTREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_STD_PREV, BUTTON_LEFT, BUTTON_NONE},
{ACTION_STD_PREVREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings */
static const struct button_mapping button_context_settings_eq[] = {
{ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_INCBIGSTEP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE},
{ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_SETTINGS_DECBIGSTEP, BUTTON_VOL_DOWN, BUTTON_NONE},
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
}; /* button_context_settings_eq */
static const struct button_mapping button_context_quickscreen[] = {
{ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE},
{ACTION_QS_DOWN, BUTTON_DOWN, BUTTON_NONE},
{ACTION_QS_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
static const struct button_mapping button_context_pitchscreen[] = {
{ACTION_PS_INC_SMALL, BUTTON_UP, BUTTON_NONE},
{ACTION_PS_INC_SMALL, BUTTON_SCROLL_BACK, BUTTON_NONE},
{ACTION_PS_INC_BIG, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_PS_DEC_SMALL, BUTTON_DOWN, BUTTON_NONE},
{ACTION_PS_DEC_SMALL, BUTTON_SCROLL_FWD, BUTTON_NONE},
{ACTION_PS_DEC_BIG, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_PS_NUDGE_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_PS_NUDGE_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_PS_NUDGE_LEFTOFF, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE},
{ACTION_PS_NUDGE_RIGHTOFF, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE},
{ACTION_PS_TOGGLE_MODE, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_PS_RESET, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
{ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE},
{ACTION_PS_FASTER, BUTTON_BACK, BUTTON_NONE},
{ACTION_PS_SLOWER, BUTTON_MENU, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_pitchscreen */
static const struct button_mapping button_context_yesnoscreen[] = {
{ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE},
{ACTION_YESNO_ACCEPT, BUTTON_SELECT, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_yesnoscreen */
static const struct button_mapping button_context_keyboard[] = {
{ACTION_KBD_UP, BUTTON_UP, BUTTON_NONE},
{ACTION_KBD_DOWN, BUTTON_DOWN, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE},
{ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE},
{ACTION_KBD_UP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_SELECT, BUTTON_SELECT, BUTTON_NONE},
{ACTION_KBD_BACKSPACE, BUTTON_BACK, BUTTON_NONE},
{ACTION_KBD_BACKSPACE, BUTTON_BACK|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_DONE, BUTTON_PLAY, BUTTON_NONE},
{ACTION_KBD_ABORT, BUTTON_POWER, BUTTON_NONE},
{ACTION_KBD_PAGE_FLIP, BUTTON_MENU, BUTTON_NONE},
{ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_KBD_CURSOR_LEFT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_UP, BUTTON_NONE},
{ACTION_KBD_CURSOR_RIGHT, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
LAST_ITEM_IN_LIST
}; /* button_context_keyboard */
const struct button_mapping* 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_MAINMENU:
case CONTEXT_BOOKMARKSCREEN:
return button_context_tree;
case CONTEXT_LIST:
return button_context_list;
case CONTEXT_SETTINGS:
case CONTEXT_SETTINGS_TIME:
case CONTEXT_SETTINGS_RECTRIGGER:
return button_context_settings;
case CONTEXT_SETTINGS_EQ:
case CONTEXT_SETTINGS_COLOURCHOOSER:
return button_context_settings_eq;
case CONTEXT_QUICKSCREEN:
return button_context_quickscreen;
case CONTEXT_PITCHSCREEN:
return button_context_pitchscreen;
case CONTEXT_YESNOSCREEN:
return button_context_yesnoscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
}
}

View file

@ -9611,15 +9611,15 @@
user: core
<source>
*: none
gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</source>
<dest>
*: none
gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</dest>
<voice>
*: none
gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>

View file

@ -9702,15 +9702,15 @@
user: core
<source>
*: none
gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</source>
<dest>
*: none
gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</dest>
<voice>
*: none
gigabeatfx,sansafuzeplus: "Touchpad Sensitivity"
gigabeatfx,sansafuzeplus,fiiom3k: "Touchpad Sensitivity"
</voice>
</phrase>
<phrase>

View file

@ -242,6 +242,12 @@
#define BATTERY_OFF_TXT "Power"
#define BATTERY_ON_TXT "Menu - start"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define BATTERY_ON BUTTON_PLAY
#define BATTERY_OFF BUTTON_POWER
#define BATTERY_ON_TXT "Play"
#define BATTERY_OFF_TXT "Power"
#else
#error "No keymap defined!"
#endif

View file

@ -607,6 +607,22 @@ enum {
#define BJACK_RIGHT BUTTON_SCROLL_FWD
#define BJACK_LEFT BUTTON_SCROLL_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define BJACK_SELECT_NAME "SELECT"
#define BJACK_STAY_NAME "PLAY"
#define BJACK_QUIT_NAME "POWER"
#define BJACK_DOUBLE_NAME "MENU"
#define BJACK_SELECT BUTTON_SELECT
#define BJACK_QUIT BUTTON_POWER
#define BJACK_MAX BUTTON_VOL_UP
#define BJACK_MIN BUTTON_VOL_DOWN
#define BJACK_STAY BUTTON_PLAY
#define BJACK_DOUBLEDOWN BUTTON_MENU
#define BJACK_UP BUTTON_UP
#define BJACK_DOWN BUTTON_DOWN
#define BJACK_RIGHT BUTTON_RIGHT
#define BJACK_LEFT BUTTON_LEFT
#else
#error No keymap defined!
#endif

View file

@ -342,6 +342,14 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_PREV
#define DOWN BUTTON_NEXT
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define SELECT BUTTON_PLAY
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#else
#error No keymap defined!
#endif

View file

@ -536,6 +536,16 @@ F3: equal to "="
#define CALCULATOR_CALC BUTTON_MENU
#define CALCULATOR_CLEAR BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CALCULATOR_LEFT BUTTON_LEFT
#define CALCULATOR_RIGHT BUTTON_RIGHT
#define CALCULATOR_UP BUTTON_UP
#define CALCULATOR_DOWN BUTTON_DOWN
#define CALCULATOR_QUIT BUTTON_POWER
#define CALCULATOR_INPUT BUTTON_PLAY
#define CALCULATOR_CALC BUTTON_MENU
#define CALCULATOR_CLEAR BUTTON_BACK
#else
#error No keymap defined!
#endif

View file

@ -421,6 +421,16 @@
#define CALENDAR_NEXT_MONTH BUTTON_VOL_UP
#define CALENDAR_PREV_MONTH BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CALENDAR_QUIT BUTTON_POWER
#define CALENDAR_SELECT BUTTON_SELECT
#define CALENDAR_NEXT_WEEK BUTTON_DOWN
#define CALENDAR_PREV_WEEK BUTTON_UP
#define CALENDAR_NEXT_DAY BUTTON_LEFT
#define CALENDAR_PREV_DAY BUTTON_RIGHT
#define CALENDAR_NEXT_MONTH BUTTON_VOL_UP
#define CALENDAR_PREV_MONTH BUTTON_VOL_DOWN
#else
#error "No keypad setting."
#endif

View file

@ -581,6 +581,20 @@
#define CB_SCROLL_LEFT (BUTTON_SCROLL_BACK|BUTTON_REPEAT)
#define CB_SCROLL_RIGHT (BUTTON_SCROLL_FWD|BUTTON_REPEAT)
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CB_SELECT BUTTON_SELECT
#define CB_UP BUTTON_UP
#define CB_DOWN BUTTON_DOWN
#define CB_LEFT BUTTON_LEFT
#define CB_RIGHT BUTTON_RIGHT
#define CB_PLAY BUTTON_PLAY
#define CB_MENU BUTTON_MENU
#define CB_LEVEL BUTTON_BACK
#define CB_SCROLL_UP (BUTTON_UP|BUTTON_REPEAT)
#define CB_SCROLL_DOWN (BUTTON_DOWN|BUTTON_REPEAT)
#define CB_SCROLL_LEFT (BUTTON_LEFT|BUTTON_REPEAT)
#define CB_SCROLL_RIGHT (BUTTON_RIGHT|BUTTON_REPEAT)
#else
#error No keymap defined!
#endif

View file

@ -406,6 +406,16 @@
#define CHC_SETTINGS_OK BUTTON_PLAY
#define CHC_SETTINGS_CANCEL BUTTON_POWER
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CHC_QUIT BUTTON_POWER
#define CHC_STARTSTOP BUTTON_PLAY
#define CHC_RESET BUTTON_BACK
#define CHC_MENU BUTTON_MENU
#define CHC_SETTINGS_INC BUTTON_UP
#define CHC_SETTINGS_DEC BUTTON_DOWN
#define CHC_SETTINGS_OK BUTTON_SELECT
#define CHC_SETTINGS_CANCEL BUTTON_POWER
#else
#error No keymap defined!
#endif

View file

@ -1283,6 +1283,14 @@ CONFIG_KEYPAD == MROBE500_PAD
#define CHIP8_KEY6 BUTTON_BACK
#define CHIP8_KEY8 BUTTON_PREV
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CHIP8_OFF BUTTON_POWER
#define CHIP8_KEY2 BUTTON_MENU
#define CHIP8_KEY4 BUTTON_LEFT
#define CHIP8_KEY5 BUTTON_SELECT
#define CHIP8_KEY6 BUTTON_RIGHT
#define CHIP8_KEY8 BUTTON_BACK
#else
#error No keymap defined!
#endif

View file

@ -203,6 +203,11 @@ CONFIG_KEYPAD == MROBE500_PAD
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define QUIT BUTTON_POWER
#define ACTION BUTTON_PLAY
#define ACTIONTEXT "PLAY"
#elif !defined(HAVE_TOUCHSCREEN)
#error No keymap defined!
#endif

View file

@ -316,6 +316,14 @@
#define CLIX_BUTTON_RIGHT BUTTON_SCROLL_FWD
#define CLIX_BUTTON_CLICK BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CLIX_BUTTON_QUIT BUTTON_POWER
#define CLIX_BUTTON_UP BUTTON_UP
#define CLIX_BUTTON_DOWN BUTTON_DOWN
#define CLIX_BUTTON_LEFT BUTTON_LEFT
#define CLIX_BUTTON_RIGHT BUTTON_RIGHT
#define CLIX_BUTTON_CLICK BUTTON_SELECT
#else
#error "no keymap"
#endif

View file

@ -410,6 +410,16 @@
#define CUBE_PAUSE BUTTON_BACK
#define CUBE_HIGHSPEED BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define CUBE_QUIT BUTTON_POWER
#define CUBE_NEXT BUTTON_RIGHT
#define CUBE_PREV BUTTON_LEFT
#define CUBE_INC BUTTON_SCROLL_FWD
#define CUBE_DEC BUTTON_SCROLL_BACK
#define CUBE_MODE BUTTON_MENU
#define CUBE_PAUSE BUTTON_PLAY
#define CUBE_HIGHSPEED BUTTON_BACK
#else
#error No keymap defined!
#endif

View file

@ -612,6 +612,18 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_WEAPON BUTTON_VOL_UP
#define DOOMBUTTON_MAP BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define DOOMBUTTON_UP BUTTON_UP
#define DOOMBUTTON_DOWN BUTTON_DOWN
#define DOOMBUTTON_LEFT BUTTON_LEFT
#define DOOMBUTTON_RIGHT BUTTON_RIGHT
#define DOOMBUTTON_SHOOT BUTTON_SELECT
#define DOOMBUTTON_OPEN BUTTON_PLAY
#define DOOMBUTTON_ESC BUTTON_POWER
#define DOOMBUTTON_ENTER BUTTON_SELECT
#define DOOMBUTTON_WEAPON BUTTON_VOL_UP
#define DOOMBUTTON_MAP BUTTON_VOL_DOWN
#else
#error Keymap not defined!
#endif

View file

@ -484,6 +484,18 @@
#define FLIPIT_STEP_BY_STEP BUTTON_VOL_UP
#define FLIPIT_TOGGLE BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define FLIPIT_LEFT BUTTON_LEFT
#define FLIPIT_RIGHT BUTTON_RIGHT
#define FLIPIT_UP BUTTON_UP
#define FLIPIT_DOWN BUTTON_DOWN
#define FLIPIT_QUIT BUTTON_POWER
#define FLIPIT_SHUFFLE BUTTON_PLAY
#define FLIPIT_SOLVE BUTTON_VOL_DOWN
#define FLIPIT_STEP_BY_STEP BUTTON_VOL_UP
#define FLIPIT_TOGGLE BUTTON_SELECT
#else
#error No keymap defined!
#endif

View file

@ -506,6 +506,18 @@
#define FRACTAL_PRECISION_DEC BUTTON_BACK
#define FRACTAL_RESET BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define FRACTAL_QUIT BUTTON_POWER
#define FRACTAL_UP BUTTON_UP
#define FRACTAL_DOWN BUTTON_DOWN
#define FRACTAL_LEFT BUTTON_LEFT
#define FRACTAL_RIGHT BUTTON_RIGHT
#define FRACTAL_ZOOM_IN BUTTON_VOL_UP
#define FRACTAL_ZOOM_OUT BUTTON_VOL_DOWN
#define FRACTAL_PRECISION_INC BUTTON_MENU
#define FRACTAL_PRECISION_DEC BUTTON_BACK
#define FRACTAL_RESET BUTTON_PLAY
#else
#error No keymap defined!
#endif

View file

@ -492,6 +492,18 @@
#define GBN_BUTTON_CONTEXT BUTTON_MENU | BUTTON_REPEAT
#define GBN_BUTTON_NEXT_VAR BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define GBN_BUTTON_UP BUTTON_UP
#define GBN_BUTTON_DOWN BUTTON_DOWN
#define GBN_BUTTON_LEFT BUTTON_LEFT
#define GBN_BUTTON_RIGHT BUTTON_RIGHT
#define GBN_BUTTON_RETREAT BUTTON_VOL_DOWN
#define GBN_BUTTON_ADVANCE BUTTON_VOL_UP
#define GBN_BUTTON_MENU BUTTON_MENU
#define GBN_BUTTON_PLAY BUTTON_PLAY
#define GBN_BUTTON_CONTEXT (BUTTON_SELECT|BUTTON_REPEAT)
#define GBN_BUTTON_NEXT_VAR BUTTON_BACK
#else
#error Unsupported keypad
#endif

View file

@ -536,6 +536,20 @@
#define IMGVIEW_MENU BUTTON_MENU
#define IMGVIEW_SLIDE_SHOW BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define IMGVIEW_ZOOM_IN BUTTON_VOL_UP
#define IMGVIEW_ZOOM_OUT BUTTON_VOL_DOWN
#define IMGVIEW_UP BUTTON_UP
#define IMGVIEW_DOWN BUTTON_DOWN
#define IMGVIEW_LEFT BUTTON_LEFT
#define IMGVIEW_RIGHT BUTTON_RIGHT
#define IMGVIEW_NEXT BUTTON_BACK
#define IMGVIEW_NEXT_REPEAT (BUTTON_BACK|BUTTON_REPEAT)
#define IMGVIEW_PREVIOUS BUTTON_MENU
#define IMGVIEW_PREVIOUS_REPEAT (BUTTON_MENU|BUTTON_REPEAT)
#define IMGVIEW_MENU BUTTON_POWER
#define IMGVIEW_SLIDE_SHOW BUTTON_PLAY
#else
#error No keymap defined!
#endif

View file

@ -296,6 +296,13 @@ CONFIG_KEYPAD == MROBE500_PAD
#define RIGHT BUTTON_SCROLL_FWD
#define FIRE BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define FIRE BUTTON_SELECT
#else
#error INVADROX: Unsupported keypad
#endif

View file

@ -387,6 +387,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define HK_SELECT "PLAY"
#define HK_CANCEL "BACK"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define JEWELS_UP BUTTON_UP
#define JEWELS_DOWN BUTTON_DOWN
#define JEWELS_LEFT BUTTON_LEFT
#define JEWELS_RIGHT BUTTON_RIGHT
#define JEWELS_SELECT BUTTON_SELECT
#define JEWELS_CANCEL BUTTON_BACK
#define HK_SELECT "SELECT"
#define HK_CANCEL "BACK"
#else
#error No keymap defined!

View file

@ -247,6 +247,14 @@
#define BTN_FIRE BUTTON_PLAY
#define BTN_PAUSE BUTTON_POWER
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define BTN_UP BUTTON_UP
#define BTN_DOWN BUTTON_DOWN
#define BTN_LEFT BUTTON_LEFT
#define BTN_RIGHT BUTTON_RIGHT
#define BTN_FIRE BUTTON_SELECT
#define BTN_PAUSE BUTTON_POWER
#else
#error Unsupported keypad
#endif

View file

@ -269,6 +269,15 @@ const struct button_mapping pla_main_ctx[] =
{ PLA_DOWN_REPEAT, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_LEFT_REPEAT, BUTTON_HOME|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_RIGHT_REPEAT, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
#elif (CONFIG_KEYPAD == FIIO_M3K_PAD)
{ PLA_UP, BUTTON_UP, BUTTON_NONE },
{ PLA_DOWN, BUTTON_DOWN, BUTTON_NONE },
{ PLA_LEFT, BUTTON_LEFT, BUTTON_NONE },
{ PLA_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
{ PLA_UP_REPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_DOWN_REPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_LEFT_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ PLA_RIGHT_REPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No directions defined
@ -506,6 +515,12 @@ const struct button_mapping pla_main_ctx[] =
{PLA_SELECT, BUTTON_PLAY, BUTTON_NONE},
{PLA_SELECT_REL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY},
{PLA_SELECT_REPEAT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_NONE},
#elif (CONFIG_KEYPAD == FIIO_M3K_PAD)
{PLA_CANCEL, BUTTON_BACK, BUTTON_NONE},
{PLA_EXIT, BUTTON_POWER, BUTTON_NONE},
{PLA_SELECT, BUTTON_SELECT, BUTTON_NONE},
{PLA_SELECT_REL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{PLA_SELECT_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE},
#else
# ifndef HAVE_TOUCHSCREEN
# error pluginlib_actions: No actions defined

View file

@ -325,6 +325,14 @@
#define MIDI_VOL_DOWN BUTTON_VOL_DOWN
#define MIDI_PLAYPAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define MIDI_QUIT BUTTON_POWER
#define MIDI_FFWD BUTTON_LEFT
#define MIDI_REWIND BUTTON_RIGHT
#define MIDI_VOL_UP BUTTON_VOL_UP
#define MIDI_VOL_DOWN BUTTON_VOL_DOWN
#define MIDI_PLAYPAUSE BUTTON_PLAY
#else
#error No keymap defined!
#endif

View file

@ -432,6 +432,17 @@ CONFIG_KEYPAD == MROBE500_PAD
# define MINESWP_DISCOVER (BUTTON_PLAY | BUTTON_REPEAT)
# define MINESWP_INFO BUTTON_VOL_UP
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
# define MINESWP_LEFT BUTTON_LEFT
# define MINESWP_RIGHT BUTTON_RIGHT
# define MINESWP_UP BUTTON_UP
# define MINESWP_DOWN BUTTON_DOWN
# define MINESWP_QUIT BUTTON_POWER
# define MINESWP_TOGGLE_PRE BUTTON_SELECT
# define MINESWP_TOGGLE (BUTTON_SELECT|BUTTON_REL)
# define MINESWP_DISCOVER (BUTTON_SELECT|BUTTON_REPEAT)
# define MINESWP_INFO BUTTON_MENU
#else
#error No keymap defined!
#endif

View file

@ -2580,6 +2580,12 @@ CONFIG_KEYPAD == MROBE500_PAD
#define MP3ENC_DONE BUTTON_POWER
#define MP3ENC_SELECT BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define MP3ENC_PREV BUTTON_LEFT
#define MP3ENC_NEXT BUTTON_RIGHT
#define MP3ENC_DONE BUTTON_POWER
#define MP3ENC_SELECT BUTTON_SELECT
#else
#error No keymap defined!
#endif

View file

@ -354,6 +354,14 @@ struct mpeg_settings settings;
#define MPEG_START_TIME_DOWN BUTTON_NEXT
#define MPEG_START_TIME_EXIT BUTTON_POWER
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define MPEG_START_TIME_SELECT BUTTON_SELECT
#define MPEG_START_TIME_LEFT BUTTON_LEFT
#define MPEG_START_TIME_RIGHT BUTTON_RIGHT
#define MPEG_START_TIME_UP BUTTON_UP
#define MPEG_START_TIME_DOWN BUTTON_DOWN
#define MPEG_START_TIME_EXIT BUTTON_POWER
#else
#error No keymap defined!
#endif

View file

@ -486,6 +486,15 @@ CONFIG_KEYPAD == SANSA_M200_PAD
#define MPEG_RW BUTTON_PREV
#define MPEG_FF BUTTON_NEXT
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define MPEG_MENU BUTTON_MENU
#define MPEG_STOP BUTTON_POWER
#define MPEG_PAUSE BUTTON_PLAY
#define MPEG_VOLDOWN BUTTON_VOL_DOWN
#define MPEG_VOLUP BUTTON_VOL_UP
#define MPEG_RW BUTTON_LEFT
#define MPEG_FF BUTTON_RIGHT
#else
#error No keymap defined!
#endif

View file

@ -537,6 +537,17 @@
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define OSCILLOSCOPE_QUIT BUTTON_POWER
#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
#define OSCILLOSCOPE_ADVMODE BUTTON_PLAY
#define OSCILLOSCOPE_ORIENTATION BUTTON_BACK
#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
#define OSCILLOSCOPE_SPEED_UP BUTTON_SCROLL_BACK
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_SCROLL_FWD
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
#else
#error No keymap defined!
#endif

View file

@ -401,6 +401,15 @@
#define PACMAN_1UP BUTTON_VOL_UP
#define PACMAN_COIN BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define PACMAN_UP BUTTON_UP
#define PACMAN_DOWN BUTTON_DOWN
#define PACMAN_LEFT BUTTON_LEFT
#define PACMAN_RIGHT BUTTON_RIGHT
#define PACMAN_MENU BUTTON_MENU
#define PACMAN_1UP BUTTON_VOL_UP
#define PACMAN_COIN BUTTON_PLAY
#else
#error Keymap not defined!

View file

@ -711,6 +711,23 @@ CONFIG_KEYPAD == MROBE500_PAD
#define LVL_UP_TEXT "VOL+"
#define LVL_DOWN_TEXT "VOL-"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define PEGBOX_SELECT BUTTON_SELECT
#define PEGBOX_QUIT BUTTON_POWER
#define PEGBOX_RESTART BUTTON_BACK
#define PEGBOX_LVL_UP BUTTON_VOL_UP
#define PEGBOX_LVL_DOWN BUTTON_VOL_DOWN
#define PEGBOX_UP BUTTON_UP
#define PEGBOX_DOWN BUTTON_DOWN
#define PEGBOX_RIGHT BUTTON_RIGHT
#define PEGBOX_LEFT BUTTON_LEFT
#define SELECT_TEXT "SELECT"
#define QUIT_TEXT "POWER"
#define RESTART_TEXT "BACK"
#define LVL_UP_TEXT "VOL+"
#define LVL_DOWN_TEXT "VOL-"
#else
#error "Unsupported keymap!"
#endif

View file

@ -76,6 +76,10 @@ OUTPUT_FORMAT(elf32-littlemips)
#elif CONFIG_CPU==IMX233
#include "cpu.h"
#define DRAMSIZE (DRAM_SIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE - FRAME_SIZE - TTB_SIZE)
#elif CONFIG_CPU==X1000
#include "config.h"
#undef STUBOFFSET
#define STUBOFFSET 0x4000
#endif
/* default to full RAM (minus codecs&plugins) unless specified otherwise */
@ -174,6 +178,11 @@ OUTPUT_FORMAT(elf32-littlemips)
#define IRAMSIZE 0
/* The bit of IRAM that is available is used in the core */
#elif CONFIG_CPU == X1000
#define DRAMORIG (0x80000000 + STUBOFFSET)
#define IRAM DRAM
#define IRAMSIZE 0
#elif CONFIG_CPU == RK27XX
#define DRAMORIG 0x60000000
#define IRAM DRAM

View file

@ -325,6 +325,13 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PONG_RIGHT_UP BUTTON_PREV
#define PONG_RIGHT_DOWN BUTTON_NEXT
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define PONG_QUIT BUTTON_POWER
#define PONG_PAUSE BUTTON_PLAY
#define PONG_LEFT_UP BUTTON_MENU
#define PONG_LEFT_DOWN BUTTON_LEFT
#define PONG_RIGHT_UP BUTTON_BACK
#define PONG_RIGHT_DOWN BUTTON_RIGHT
#else
#error No keymap defined!

View file

@ -361,6 +361,15 @@
#define REVERSI_BUTTON_MAKE_MOVE BUTTON_PLAY
#define REVERSI_BUTTON_MENU BUTTON_MENU
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define REVERSI_BUTTON_QUIT BUTTON_POWER
#define REVERSI_BUTTON_UP BUTTON_UP
#define REVERSI_BUTTON_DOWN BUTTON_DOWN
#define REVERSI_BUTTON_LEFT BUTTON_LEFT
#define REVERSI_BUTTON_RIGHT BUTTON_RIGHT
#define REVERSI_BUTTON_MAKE_MOVE BUTTON_SELECT
#define REVERSI_BUTTON_MENU BUTTON_MENU
#else
#error No keymap defined!
#endif

View file

@ -477,6 +477,16 @@
#define ROCKBLOX_DROP BUTTON_PLAY
#define ROCKBLOX_RESTART BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define ROCKBLOX_OFF BUTTON_POWER
#define ROCKBLOX_ROTATE_CCW BUTTON_UP
#define ROCKBLOX_ROTATE_CW BUTTON_DOWN
#define ROCKBLOX_DOWN BUTTON_SELECT
#define ROCKBLOX_LEFT BUTTON_LEFT
#define ROCKBLOX_RIGHT BUTTON_RIGHT
#define ROCKBLOX_DROP BUTTON_PLAY
#define ROCKBLOX_RESTART BUTTON_BACK
#else
#error No keymap defined!
#endif

View file

@ -467,6 +467,17 @@ static void setoptions (void)
options.SELECT = BUTTON_VOL_UP;
options.MENU = BUTTON_POWER;
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
options.UP = BUTTON_UP;
options.DOWN = BUTTON_DOWN;
options.LEFT = BUTTON_LEFT;
options.RIGHT = BUTTON_RIGHT;
options.A = BUTTON_MENU;
options.B = BUTTON_BACK;
options.START = BUTTON_VOL_DOWN;
options.SELECT = BUTTON_VOL_UP;
options.MENU = BUTTON_POWER;
#else
#error No Keymap Defined!
#endif

View file

@ -393,6 +393,17 @@
#define ROCKPAINT_LEFT BUTTON_HOME
#define ROCKPAINT_RIGHT BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_MENU
#define ROCKPAINT_TOOLBAR BUTTON_VOL_UP
#define ROCKPAINT_TOOLBAR2 BUTTON_VOL_DOWN
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#else
#error "Please define keys for this keypad"
#endif

View file

@ -348,6 +348,14 @@ CONFIG_KEYPAD == MROBE500_PAD
#define PUZZLE_SHUFFLE BUTTON_BACK
#define PUZZLE_PICTURE BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define PUZZLE_QUIT BUTTON_POWER
#define PUZZLE_LEFT BUTTON_LEFT
#define PUZZLE_RIGHT BUTTON_RIGHT
#define PUZZLE_UP BUTTON_UP
#define PUZZLE_DOWN BUTTON_DOWN
#define PUZZLE_SHUFFLE BUTTON_BACK
#define PUZZLE_PICTURE BUTTON_PLAY
#else
#error No keymap defined!

View file

@ -305,6 +305,14 @@ dir is the current direction of the snake - 0=up, 1=right, 2=down, 3=left;
#define SNAKE_DOWN BUTTON_NEXT
#define SNAKE_PLAYPAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define SNAKE_QUIT BUTTON_POWER
#define SNAKE_LEFT BUTTON_LEFT
#define SNAKE_RIGHT BUTTON_RIGHT
#define SNAKE_UP BUTTON_UP
#define SNAKE_DOWN BUTTON_DOWN
#define SNAKE_PLAYPAUSE BUTTON_PLAY
#else
#error No keymap defined!
#endif

View file

@ -437,6 +437,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "PLAY"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define SNAKE2_LEFT BUTTON_LEFT
#define SNAKE2_RIGHT BUTTON_RIGHT
#define SNAKE2_UP BUTTON_UP
#define SNAKE2_DOWN BUTTON_DOWN
#define SNAKE2_QUIT BUTTON_POWER
#define SNAKE2_PLAYPAUSE BUTTON_PLAY
#define SNAKE2_PLAYPAUSE_TEXT "PLAY"
#else
#error No keymap defined!
#endif

View file

@ -695,6 +695,21 @@
#define BUTTON_SAVE BUTTON_PLAY
#define BUTTON_SAVE_NAME "PLAY"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define SOKOBAN_LEFT BUTTON_LEFT
#define SOKOBAN_RIGHT BUTTON_RIGHT
#define SOKOBAN_UP BUTTON_UP
#define SOKOBAN_DOWN BUTTON_DOWN
#define SOKOBAN_MENU BUTTON_MENU
#define SOKOBAN_UNDO BUTTON_VOL_DOWN
#define SOKOBAN_REDO BUTTON_VOL_UP
#define SOKOBAN_LEVEL_DOWN (BUTTON_PLAY|BUTTON_VOL_DOWN)
#define SOKOBAN_LEVEL_UP (BUTTON_PLAY|BUTTON_VOL_UP)
#define SOKOBAN_LEVEL_REPEAT (BUTTON_PLAY|BUTTON_POWER)
#define SOKOBAN_PAUSE BUTTON_SELECT
#define BUTTON_SAVE BUTTON_BACK
#define BUTTON_SAVE_NAME "BACK"
#else
#error No keymap defined!
#endif

View file

@ -722,6 +722,25 @@ CONFIG_KEYPAD == MROBE500_PAD
# define HK_CUR2STACK "DBL PLAY"
# define HK_REM2STACK "NEXT"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
# define SOL_QUIT BUTTON_POWER
# define SOL_UP BUTTON_UP
# define SOL_DOWN BUTTON_DOWN
# define SOL_LEFT BUTTON_LEFT
# define SOL_RIGHT BUTTON_RIGHT
# define SOL_MOVE_PRE BUTTON_SELECT
# define SOL_MOVE (BUTTON_SELECT|BUTTON_REL)
# define SOL_DRAW BUTTON_PLAY
# define SOL_REM2CUR BUTTON_VOL_DOWN
# define SOL_CUR2STACK_PRE BUTTON_SELECT
# define SOL_CUR2STACK (BUTTON_SELECT|BUTTON_REPEAT)
# define SOL_REM2STACK BUTTON_VOL_UP
# define HK_MOVE "SELECT"
# define HK_DRAW "PLAY"
# define HK_REM2CUR "VOL-"
# define HK_CUR2STACK "HOLD SELECT"
# define HK_REM2STACK "VOL+"
#else
#error No keymap defined!
#endif

View file

@ -363,6 +363,15 @@
#define AST_RIGHT BUTTON_SCROLL_FWD
#define AST_FIRE BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define AST_PAUSE BUTTON_MENU
#define AST_QUIT BUTTON_POWER
#define AST_THRUST BUTTON_SELECT
#define AST_HYPERSPACE BUTTON_BACK
#define AST_LEFT BUTTON_LEFT
#define AST_RIGHT BUTTON_RIGHT
#define AST_FIRE BUTTON_PLAY
#else
#error No keymap defined!
#endif

View file

@ -652,6 +652,22 @@
#define STAR_LEVEL_DOWN_NAME "VOL DN"
#define STAR_LEVEL_REPEAT_NAME "BACK"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define STAR_QUIT BUTTON_POWER
#define STAR_LEFT BUTTON_LEFT
#define STAR_RIGHT BUTTON_RIGHT
#define STAR_UP BUTTON_UP
#define STAR_DOWN BUTTON_DOWN
#define STAR_TOGGLE_CONTROL BUTTON_PLAY
#define STAR_LEVEL_UP BUTTON_VOL_UP
#define STAR_LEVEL_DOWN BUTTON_VOL_DOWN
#define STAR_LEVEL_REPEAT BUTTON_BACK
#define STAR_TOGGLE_CONTROL_NAME "PLAY"
#define STAR_QUIT_NAME "POWER"
#define STAR_LEVEL_UP_NAME "VOL+"
#define STAR_LEVEL_DOWN_NAME "VOL-"
#define STAR_LEVEL_REPEAT_NAME "BACK"
#else
#error No keymap defined!
#endif

View file

@ -309,6 +309,14 @@
#define STOPWATCH_SCROLL_UP BUTTON_SCROLL_FWD
#define STOPWATCH_SCROLL_DOWN BUTTON_SCROLL_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define STOPWATCH_QUIT BUTTON_POWER
#define STOPWATCH_START_STOP BUTTON_PLAY
#define STOPWATCH_RESET_TIMER BUTTON_BACK
#define STOPWATCH_LAP_TIMER BUTTON_SELECT
#define STOPWATCH_SCROLL_UP BUTTON_SCROLL_BACK
#define STOPWATCH_SCROLL_DOWN BUTTON_SCROLL_FWD
#else
#error No keymap defined!
#endif

View file

@ -459,6 +459,17 @@
#define SUDOKU_BUTTON_MENU (BUTTON_MENU | BUTTON_REL)
#define SUDOKU_BUTTON_POSSIBLE BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define SUDOKU_BUTTON_QUIT BUTTON_POWER
#define SUDOKU_BUTTON_UP BUTTON_UP
#define SUDOKU_BUTTON_DOWN BUTTON_DOWN
#define SUDOKU_BUTTON_LEFT BUTTON_LEFT
#define SUDOKU_BUTTON_RIGHT BUTTON_RIGHT
#define SUDOKU_BUTTON_TOGGLEBACK BUTTON_VOL_DOWN
#define SUDOKU_BUTTON_TOGGLE BUTTON_VOL_UP
#define SUDOKU_BUTTON_MENU BUTTON_MENU
#define SUDOKU_BUTTON_POSSIBLE BUTTON_BACK
#else
#error No keymap defined!
#endif

View file

@ -583,6 +583,18 @@
#define TV_LINE_DOWN (BUTTON_POWER | BUTTON_VOL_DOWN)
#define TV_BOOKMARK (BUTTON_POWER | BUTTON_PLAY)
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define TV_QUIT BUTTON_POWER
#define TV_SCROLL_UP BUTTON_VOL_UP
#define TV_SCROLL_DOWN BUTTON_VOL_DOWN
#define TV_SCREEN_LEFT BUTTON_LEFT
#define TV_SCREEN_RIGHT BUTTON_RIGHT
#define TV_MENU BUTTON_MENU
#define TV_AUTOSCROLL BUTTON_SELECT
#define TV_LINE_UP BUTTON_SCROLL_BACK
#define TV_LINE_DOWN BUTTON_SCROLL_FWD
#define TV_BOOKMARK BUTTON_PLAY
#else
#error No keymap defined!
#endif

View file

@ -441,6 +441,17 @@
#define LABEL_MENU "MENU"
#define LABEL_VOLUME "VOL UP/DN"
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define VUMETER_QUIT BUTTON_POWER
#define VUMETER_HELP BUTTON_BACK
#define VUMETER_MENU BUTTON_MENU
#define VUMETER_UP BUTTON_VOL_UP
#define VUMETER_DOWN BUTTON_VOL_DOWN
#define LABEL_HELP "BACK"
#define LABEL_QUIT "POWER"
#define LABEL_MENU "MENU"
#define LABEL_VOLUME "VOL+/VOL-"
#else
#error No keymap defined!
#endif

View file

@ -398,6 +398,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define BTN_QUIT BUTTON_POWER
#define BTN_STOPRESET BUTTON_BACK
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define BTN_DIR_UP BUTTON_UP
#define BTN_DIR_DOWN BUTTON_DOWN
#define BTN_DIR_LEFT BUTTON_LEFT
#define BTN_DIR_RIGHT BUTTON_RIGHT
#define BTN_STARTPAUSE BUTTON_PLAY
#define BTN_QUIT BUTTON_POWER
#define BTN_STOPRESET BUTTON_BACK
#else
#error No keymap defined!
#endif

View file

@ -351,6 +351,15 @@ CONFIG_KEYPAD == MROBE500_PAD
#define DOWN BUTTON_NEXT
#define PAUSE BUTTON_PLAY
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
#define PAUSE BUTTON_PLAY
#else
#error "No keymap defined!"
#endif

View file

@ -298,6 +298,14 @@
#define ZX_UP BUTTON_PREV
#define ZX_DOWN BUTTON_NEXT
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define ZX_SELECT BUTTON_SELECT
#define ZX_MENU BUTTON_MENU
#define ZX_LEFT BUTTON_LEFT
#define ZX_RIGHT BUTTON_RIGHT
#define ZX_UP BUTTON_UP
#define ZX_DOWN BUTTON_DOWN
#else
#error Keymap not defined!

View file

@ -293,6 +293,15 @@
#define KBD_UP BUTTON_PREV
#define KBD_DOWN BUTTON_NEXT
#elif CONFIG_KEYPAD == FIIO_M3K_PAD
#define KBD_SELECT BUTTON_SELECT
#define KBD_ABORT BUTTON_BACK
#define KBD_LEFT BUTTON_LEFT
#define KBD_RIGHT BUTTON_RIGHT
#define KBD_UP BUTTON_UP
#define KBD_DOWN BUTTON_DOWN
#endif
#ifdef HAVE_TOUCHSCREEN

View file

@ -89,4 +89,11 @@ show_logo.c
#elif defined(SANSA_CONNECT)
sansaconnect.c
show_logo.c
#elif defined(FIIO_M3K)
#ifdef BOOTLOADER_SPL
x1000-spl.c
fiiom3k-spl.c
#else
fiiom3k.c
#endif
#endif

204
bootloader/fiiom3k-spl.c Normal file
View file

@ -0,0 +1,204 @@
/***************************************************************************
* __________ __ ___.
* 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 "config.h"
#include "nand-x1000.h"
#include "gpio-x1000.h"
#include "mmu-mips.h"
#include <string.h>
/* "fiio" in little endian */
#define BOOTMAGIC 0x6f696966
/* Argument structure needed by Linux */
struct linux_kargs {
void* arg0;
void* arg1;
};
#define LINUX_KARGSADDR 0x80004000
static const char recovery_cmdline[] = "mem=xxM@0x0\
no_console_suspend\
console=ttyS2,115200n8\
lpj=5009408\
ip=off";
static const char normal_cmdline[] = "mem=64M@0x0\
no_console_suspend\
console=ttyS2,115200n8\
lpj=5009408\
ip=off\
init=/linuxrc\
ubi.mtd=3\
root=ubi0:rootfs\
ubi.mtd=4\
rootfstype=ubifs\
rw\
loglevel=8";
#define BOOTOPTION_ROCKBOX 0
#define BOOTOPTION_FIIOLINUX 1
#define BOOTOPTION_RECOVERY 2
#define NUM_BOOTOPTIONS 3
static const struct bootoption {
uint32_t nand_addr;
uint32_t nand_size;
unsigned long load_addr;
unsigned long exec_addr;
const char* cmdline;
} boot_options[NUM_BOOTOPTIONS] = {
{
/* Rockbox: the first unused NAND page is 26 KiB in, and the
* remainder of the block is unused, giving us 102 KiB to use.
*/
.nand_addr = 0x6800,
.nand_size = 0x19800,
.load_addr = 0x80003ff8, /* first 8 bytes are bootloader ID */
.exec_addr = 0x80004000,
.cmdline = NULL,
},
{
/* Original firmware */
.nand_addr = 0x20000,
.nand_size = 0x400000,
.load_addr = 0x80efffc0,
.exec_addr = 0x80f00000,
.cmdline = normal_cmdline,
},
{
/* Recovery image */
.nand_addr = 0x420000,
.nand_size = 0x500000,
.load_addr = 0x80efffc0,
.exec_addr = 0x80f00000,
.cmdline = recovery_cmdline,
},
};
/* Simple diagnostic if something goes wrong -- a little nicer than wondering
* what's going on when the machine hangs
*/
void die(void)
{
const int pin = (1 << 24);
/* Turn on button light */
jz_clr(GPIO_INT(GPIO_C), pin);
jz_set(GPIO_MSK(GPIO_C), pin);
jz_clr(GPIO_PAT1(GPIO_C), pin);
jz_set(GPIO_PAT0(GPIO_C), pin);
while(1) {
/* Turn it off */
mdelay(100);
jz_set(GPIO_PAT0(GPIO_C), pin);
/* Turn it on */
mdelay(100);
jz_clr(GPIO_PAT0(GPIO_C), pin);
}
}
/* Boot select button state must remain stable for this duration
* before the choice will be accepted. Currently 100ms.
*/
#define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000))
int get_boot_option(void)
{
const uint32_t pinmask = (1 << 17) | (1 << 19);
uint32_t pin = 1, lastpin = 0;
uint32_t deadline = 0;
/* Configure the button GPIOs as inputs */
gpio_config(GPIO_A, pinmask, GPIO_INPUT);
/* Poll the pins for a short duration to detect a keypress */
do {
lastpin = pin;
pin = ~REG_GPIO_PIN(GPIO_A) & pinmask;
if(pin != lastpin) {
/* This will always be set on the first iteration */
deadline = __ost_read32() + BTN_STABLE_TIME;
}
} while(__ost_read32() < deadline);
/* Play button boots original firmware */
if(pin == (1 << 17))
return BOOTOPTION_FIIOLINUX;
/* Volume up boots recovery */
if(pin == (1 << 19))
return BOOTOPTION_RECOVERY;
/* Default is to boot Rockbox */
return BOOTOPTION_ROCKBOX;
}
void spl_main(void)
{
/* Get user boot option */
int booti = get_boot_option();
const struct bootoption* opt = &boot_options[booti];
/* Load selected firmware from flash */
if(nand_open())
die();
if(nand_read_bytes(opt->nand_addr, opt->nand_size, (void*)opt->load_addr))
die();
if(booti == BOOTOPTION_ROCKBOX) {
/* If bootloader is not installed, return back to boot ROM.
* Also read in the first eraseblock of NAND flash so it can be
* dumped back over USB.
*/
if(*(unsigned*)(opt->load_addr + 4) != BOOTMAGIC) {
nand_read_bytes(0, 128 * 1024, (void*)0x80000000);
commit_discard_idcache();
return;
}
} else {
/* TODO: Linux boot not implemented yet
*
* - Have to initialize UART2, as it's used for the serial console
* - Must initialize APLL and change clocks over
* - There are some other clocks which need to be initialized
* - We should turn off OST since the OF SPL does not turn it on
*/
die();
}
if(boot_options[booti].cmdline) {
/* Handle Linux command line arguments */
struct linux_kargs* kargs = (struct linux_kargs*)LINUX_KARGSADDR;
kargs->arg0 = 0;
kargs->arg1 = (void*)boot_options[booti].cmdline;
}
/* Flush caches and jump to address */
void* execaddr = (void*)opt->exec_addr;
commit_discard_idcache();
__asm__ __volatile__ ("jr %0" :: "r"(execaddr));
__builtin_unreachable();
}

96
bootloader/fiiom3k.c Normal file
View file

@ -0,0 +1,96 @@
/***************************************************************************
* __________ __ ___.
* 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 "kernel/kernel-internal.h"
#include "i2c.h"
#include "power.h"
#include "lcd.h"
#include "backlight.h"
#include "button.h"
#include "storage.h"
#include "file_internal.h"
#include "disk.h"
#include "rb-loader.h"
#include "loader_strerror.h"
/* Load address where the binary needs to be placed */
extern unsigned char loadaddress[];
/* Fixed buffer to contain the loaded binary in memory */
extern unsigned char loadbuffer[];
extern unsigned char loadbufferend[];
#define MAX_LOAD_SIZE (loadbufferend - loadbuffer)
void exec(void* dst, const void* src, int bytes)
__attribute__((noreturn, section(".icode")));
void exec(void* dst, const void* src, int bytes)
{
memcpy(dst, src, bytes);
commit_discard_idcache();
__asm__ __volatile__ ("jr %0" :: "r"(dst));
__builtin_unreachable();
}
static void error(const char* msg)
{
/* Initialization of the LCD/buttons only if needed */
lcd_init();
backlight_init();
button_init();
lcd_clear_display();
lcd_puts(0, 0, msg);
lcd_puts(0, 2, "Press POWER to power off");
lcd_update();
while(button_get(true) != BUTTON_POWER);
power_off();
}
void main(void)
{
system_init();
kernel_init();
i2c_init();
power_init();
enable_irq();
if(storage_init() < 0)
error("Storage initialization failed");
filesystem_init();
if(!storage_present(0))
error("No SD card present");
if(disk_mount_all() <= 0)
error("Unable to mount filesystem");
int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
if(loadsize <= 0)
error(loader_strerror(loadsize));
disable_irq();
exec(loadaddress, loadbuffer, loadsize);
}

230
bootloader/x1000-spl.c Normal file
View file

@ -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.
*
****************************************************************************/
#include "system.h"
#include "clk-x1000.h"
#include "x1000/cpm.h"
#include "x1000/ost.h"
#include "x1000/ddrc.h"
#include "x1000/ddrc_apb.h"
#include "x1000/ddrphy.h"
#ifdef FIIO_M3K
# define DDR_USE_AUTOSR 1
# define DDR_NEED_BYPASS 1
# define DDR_MEMORYSIZE 64
#else
# error "Please add DDR definitions for new target!"
#endif
#define hang() do { } while(1)
/* Target-specific routine to load & execute the Rockbox bootloader */
extern void spl_main(void);
/* Note: This is based purely on disassembly of the SPL from the FiiO M3K.
* The code there is somewhat generic and corresponds roughly to Ingenic's
* U-Boot code, but isn't entirely the same.
*
* I converted all the runtime conditionals to compile-time ones in order to
* save code space, since they should be constant for any given target.
*
* I haven't bothered to decode all the register fields. Some of the values
* written are going to bits documented as "Reserved" by Ingenic, but their
* documentation doesn't seem completely reliable, so either these are bits
* which _do_ have a purpose, or they're only defined on other Ingenic CPUs.
*
* The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite".
* These aren't documented by Ingenic, but the addresses and names can be found
* in their U-Boot code.
*/
static void ddr_init(void)
{
REG_CPM_DRCG = 0x73;
mdelay(3);
REG_CPM_DRCG = 0x71;
mdelay(3);
REG_DDRC_APB_PHYRST_CFG = 0x1a00001;
mdelay(3);
REG_DDRC_APB_PHYRST_CFG = 0;
mdelay(3);
REG_DDRC_CTRL = 0xf00000;
mdelay(3);
REG_DDRC_CTRL = 0;
mdelay(3);
REG_DDRC_CFG = 0xa468a6c;
REG_DDRC_CTRL = 2;
REG_DDRPHY_DTAR = 0x150000;
REG_DDRPHY_DCR = 0;
REG_DDRPHY_MR0 = 0x42;
REG_DDRPHY_MR2 = 0x98;
REG_DDRPHY_PTR0 = 0x21000a;
REG_DDRPHY_PTR1 = 0xa09c40;
REG_DDRPHY_PTR2 = 0x280014;
REG_DDRPHY_DTPR0 = 0x1a69444a;
REG_DDRPHY_DTPR1 = 0x180090;
REG_DDRPHY_DTPR2 = 0x1ff99428;
REG_DDRPHY_DXGCR(0) = 0x90881;
REG_DDRPHY_DXGCR(1) = 0x90881;
REG_DDRPHY_DXGCR(2) = 0x90e80;
REG_DDRPHY_DXGCR(3) = 0x90e80;
REG_DDRPHY_PGCR = 0x1042e03;
REG_DDRPHY_ACIOCR = 0x30c00813;
REG_DDRPHY_DXCCR = 0x4912;
int i = 10000;
while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
i -= 1;
if(i == 0)
hang();
#if DDR_NEED_BYPASS
REG_DDRPHY_ACDLLCR = 0x80000000;
REG_DDRPHY_DSGCR &= ~0x10;
REG_DDRPHY_DLLGCR |= 0x800000;
REG_DDRPHY_PIR = 0x20020041;
#else
REG_DDRPHY_PIR = 0x41;
#endif
while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
i -= 1;
if(i == 0)
hang();
REG_DDRC_APB_PHYRST_CFG = 0x400000;
mdelay(3);
REG_DDRC_APB_PHYRST_CFG = 0;
mdelay(3);
REG_DDRC_CFG = 0xa468aec;
REG_DDRC_CTRL = 2;
#if DDR_NEED_BYPASS
REG_DDRPHY_PIR = 0x20020081;
#else
REG_DDRPHY_PIR = 0x85;
#endif
i = 500000;
while(REG_DDRPHY_PGSR != 0x1f) {
if(REG_DDRPHY_PGSR & 0x70)
break;
i -= 1;
}
if(i == 0)
hang();
if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
hang();
REG_DDRC_CTRL = 0;
REG_DDRC_CTRL = 10;
REG_DDRC_CTRL = 0;
REG_DDRC_CFG = 0xa468a6c;
REG_DDRC_TIMING1 = 0x2050501;
REG_DDRC_TIMING2 = 0x4090404;
REG_DDRC_TIMING3 = 0x2704030d;
REG_DDRC_TIMING4 = 0xb7a0251;
REG_DDRC_TIMING5 = 0xff090200;
REG_DDRC_TIMING6 = 0xa0a0202;
#if DDR_MEMORYSIZE == 64
REG_DDRC_MMAP0 = 0x20fc;
REG_DDRC_MMAP1 = 0x2400;
#elif DDR_MEMORYSIZE == 32
REG_DDRC_MMAP0 = 0x20fe;
REG_DDRC_MMAP1 = 0x2200;
#else
# error "Unsupported DDR_MEMORYSIZE"
#endif
REG_DDRC_CTRL = 10;
REG_DDRC_REFCNT = 0x2f0003;
REG_DDRC_CTRL = 0xc91e;
#if DDR_MEMORYSIZE == 64
REG_DDRC_REMAP1 = 0x03020c0b;
REG_DDRC_REMAP2 = 0x07060504;
REG_DDRC_REMAP3 = 0x000a0908;
REG_DDRC_REMAP4 = 0x0f0e0d01;
REG_DDRC_REMAP5 = 0x13121110;
#elif DDR_MEMORYSIZE == 32
REG_DDRC_REMAP1 = 0x03020b0a;
REG_DDRC_REMAP2 = 0x07060504;
REG_DDRC_REMAP3 = 0x01000908;
REG_DDRC_REMAP4 = 0x0f0e0d0c;
REG_DDRC_REMAP5 = 0x13121110;
#else
# error "Unsupported DDR_MEMORYSIZE"
#endif
REG_DDRC_STATUS &= ~0x40;
#if DDR_USE_AUTOSR
#if DDR_NEED_BYPASS
jz_writef(CPM_DDRCDR, GATE_EN(1));
REG_DDRC_APB_CLKSTP_CFG = 0x9000000f;
#else
REG_DDRC_DLP = 0;
#endif
#endif
REG_DDRC_AUTOSR_EN = DDR_USE_AUTOSR;
}
void main(void)
{
/* from original firmware SPL */
REG_CPM_PSWC0ST = 0x00;
REG_CPM_PSWC1ST = 0x10;
REG_CPM_PSWC2ST = 0x18;
REG_CPM_PSWC3ST = 0x08;
/* enable MPLL */
#if X1000_EXCLK_FREQ == 24000000
/* 24 * (24+1) = 600 MHz */
jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0));
#elif X1000_EXCLK_FREQ == 26000000
/* 26 * (22+1) = 598 MHz */
jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0));
#else
# error "unknown EXCLK frequency"
#endif
while(jz_readf(CPM_MPCR, ON) == 0);
/* set DDR clock to MPLL/3 = 200 MHz */
jz_writef(CPM_CLKGR, DDR(0));
clk_set_ddr(X1000_CLK_MPLL, 3);
/* start OST so we can use mdelay/udelay */
jz_writef(CPM_CLKGR, OST(0));
jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
jz_writef(OST_CLEAR, OST2(1));
jz_write(OST_2CNTH, 0);
jz_write(OST_2CNTL, 0);
jz_setf(OST_ENABLE, OST2);
/* init DDR memory */
ddr_init();
/* jump to the target's main routine */
spl_main();
}

View file

@ -398,6 +398,8 @@ drivers/rtc/rtc_tcc77x.c
drivers/rtc/rtc_jz4740.c
#elif (CONFIG_RTC == RTC_JZ4760)
drivers/rtc/rtc_jz4760.c
#elif (CONFIG_RTC == RTC_X1000)
drivers/rtc/rtc_x1000.c
#elif (CONFIG_RTC == RTC_S35390A)
drivers/rtc/rtc_s35390a.c
#elif (CONFIG_RTC == RTC_S35380A)
@ -488,6 +490,8 @@ drivers/audio/as3514.c
drivers/audio/tlv320.c
#elif defined(HAVE_AK4537)
drivers/audio/ak4537.c
#elif defined(HAVE_AK4376)
drivers/audio/ak4376.c
#elif defined(HAVE_UDA1341)
drivers/audio/uda1341.c
#elif defined(HAVE_CS42L55)
@ -1721,6 +1725,30 @@ target/mips/ingenic_jz47xx/pcm-jz4760.c
drivers/nand_id.c
#endif /* CONFIG_CPU == JZ4760B */
#if CONFIG_CPU == X1000
target/mips/ingenic_x1000/crt0.S
target/mips/ingenic_x1000/aic-x1000.c
target/mips/ingenic_x1000/clk-x1000.c
target/mips/ingenic_x1000/debug-x1000.c
target/mips/ingenic_x1000/dma-x1000.c
target/mips/ingenic_x1000/gpio-x1000.c
target/mips/ingenic_x1000/i2c-x1000.c
target/mips/ingenic_x1000/kernel-x1000.c
target/mips/ingenic_x1000/lcd-x1000.c
target/mips/ingenic_x1000/nand-x1000.c
target/mips/ingenic_x1000/pcm-x1000.c
target/mips/ingenic_x1000/pwm-x1000.c
target/mips/ingenic_x1000/sfc-x1000.c
target/mips/ingenic_x1000/system-x1000.c
target/mips/ingenic_x1000/timer-x1000.c
#if (CONFIG_STORAGE & (STORAGE_SD|STORAGE_MMC|STORAGE_ATA))
target/mips/ingenic_x1000/msc-x1000.c
#endif
#if (CONFIG_STORAGE & STORAGE_SD)
target/mips/ingenic_x1000/sd-x1000.c
#endif
#endif /* CONFIG_CPU == X1000 */
#if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777)
target/mips/ingenic_jz47xx/onda_vx747/backlight-onda_vx7X7.c
target/mips/ingenic_jz47xx/onda_vx747/lcd-onda_vx747.c
@ -1744,6 +1772,16 @@ target/mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c
target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c
#endif /* XDUOO_X3 */
#if defined(FIIO_M3K)
target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c
#endif /* FIIO_M3K */
#if defined(LYRE_PROTO1)
target/arm/at91sam/lyre_proto1/adc-lyre_proto1.c
target/arm/at91sam/lyre_proto1/backlight-lyre_proto1.c
@ -1969,6 +2007,10 @@ drivers/touchpad.c
drivers/i2c-async.c
#endif
#ifdef HAVE_AXP173
drivers/axp173.c
#endif
/* firmware/kernel section */
#ifdef HAVE_CORELOCK_OBJECT
kernel/corelock.c

View file

@ -0,0 +1,274 @@
/***************************************************************************
* __________ __ ___.
* 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 "sound.h"
#include "panic.h"
#include "pcm_sampr.h"
#include "pcm_sw_volume.h"
#include "system.h"
#include "i2c-async.h"
#ifndef HAVE_SW_VOLUME_CONTROL
# error "AK4376 requires HAVE_SW_VOLUME_CONTROL!"
#endif
/* NOTE: At present, only the FiiO M3K uses this driver so the handling of
* the clock / audio interface is limited to I2S slave, 16-bit samples, with
* DAC master clock provided directly on the MCLK input pin, fitting the
* clock setup of the M3K.
*
* Feel free to expand upon this if another target ever needs this driver.
*/
/* Converts HW_FREQ_XX constants to register values */
static const int ak4376_fsel_to_hw[] = {
HW_HAVE_192_(AK4376_FS_192,)
HW_HAVE_176_(AK4376_FS_176,)
HW_HAVE_96_(AK4376_FS_96,)
HW_HAVE_88_(AK4376_FS_88,)
HW_HAVE_64_(AK4376_FS_64,)
HW_HAVE_48_(AK4376_FS_48,)
HW_HAVE_44_(AK4376_FS_44,)
HW_HAVE_32_(AK4376_FS_32,)
HW_HAVE_24_(AK4376_FS_24,)
HW_HAVE_22_(AK4376_FS_22,)
HW_HAVE_16_(AK4376_FS_16,)
HW_HAVE_12_(AK4376_FS_12,)
HW_HAVE_11_(AK4376_FS_11,)
HW_HAVE_8_(AK4376_FS_8,)
};
static struct ak4376 {
int fsel;
int low_mode;
int regs[AK4376_NUM_REGS];
} ak4376;
void ak4376_init(void)
{
/* Initialize DAC state */
ak4376.fsel = HW_FREQ_48;
ak4376.low_mode = 0;
for(int i = 0; i < AK4376_NUM_REGS; ++i)
ak4376.regs[i] = -1;
/* Initial reset after power-on */
ak4376_set_pdn_pin(0);
mdelay(1);
ak4376_set_pdn_pin(1);
mdelay(1);
static const int init_config[] = {
/* Ensure HPRHZ, HPLHZ are 0 */
AK4376_REG_OUTPUT_MODE, 0x00,
/* Mute all volume controls */
AK4376_REG_MIXER, 0x00,
AK4376_REG_LCH_VOLUME, 0x80,
AK4376_REG_RCH_VOLUME, 0x00,
AK4376_REG_AMP_VOLUME, 0x00,
/* Clock source = MCLK, divider = 1 */
AK4376_REG_DAC_CLK_SRC, 0x00,
AK4376_REG_DAC_CLK_DIV, 0x00,
/* I2S slave mode, 16-bit samples */
AK4376_REG_AUDIO_IF_FMT, 0x03,
/* Recommended by datasheet */
AK4376_REG_ADJUST1, 0x20,
AK4376_REG_ADJUST2, 0x05,
/* Power controls */
AK4376_REG_PWR2, 0x33,
AK4376_REG_PWR3, 0x01,
AK4376_REG_PWR4, 0x03,
};
/* Write initial configuration prior to power-up */
for(size_t i = 0; i < ARRAYLEN(init_config); i += 2)
ak4376_write(init_config[i], init_config[i+1]);
/* Initial frequency setting, also handles DAC/amp power-up */
audiohw_set_frequency(HW_FREQ_48);
}
void ak4376_close(void)
{
/* Shut off power */
ak4376_write(AK4376_REG_PWR3, 0x00);
ak4376_write(AK4376_REG_PWR4, 0x00);
ak4376_write(AK4376_REG_PWR2, 0x00);
/* PDN pin low */
ak4376_set_pdn_pin(0);
}
void ak4376_write(int reg, int value)
{
/* Ensure value is sensible and differs from the last set value */
if((value & 0xff) == value && ak4376.regs[reg] != value) {
int r = i2c_reg_write1(AK4376_BUS, AK4376_ADDR, reg, value);
if(r == I2C_STATUS_OK)
ak4376.regs[reg] = value;
else
ak4376.regs[reg] = -1;
}
}
int ak4376_read(int reg)
{
/* Only read from I2C if we don't already know the value */
if(ak4376.regs[reg] < 0)
ak4376.regs[reg] = i2c_reg_read1(AK4376_BUS, AK4376_ADDR, reg);
return ak4376.regs[reg];
}
static int round_step_up(int x, int step)
{
int rem = x % step;
if(rem > 0)
rem -= step;
return x - rem;
}
static void calc_volumes(int vol, int* mix, int* dig, int* sw)
{
/* Mixer can divide by 2, which gives an extra -6 dB adjustment */
if(vol < AK4376_DIG_VOLUME_MIN) {
*mix |= AK4376_MIX_HALF;
vol += 60;
}
*dig = round_step_up(vol, AK4376_DIG_VOLUME_STEP);
*dig = MIN(*dig, AK4376_DIG_VOLUME_MAX);
*dig = MAX(*dig, AK4376_DIG_VOLUME_MIN);
vol -= *dig;
/* Seems that this is the allowable range for software volume */
*sw = MIN(vol, 60);
*sw = MAX(*sw, -730);
vol -= *sw;
}
static int dig_vol_to_hw(int vol)
{
if(vol < AK4376_DIG_VOLUME_MIN) return 0;
if(vol > AK4376_DIG_VOLUME_MAX) return 31;
return (vol - AK4376_DIG_VOLUME_MIN) / AK4376_DIG_VOLUME_STEP + 1;
}
static int amp_vol_to_hw(int vol)
{
if(vol < AK4376_AMP_VOLUME_MIN) return 0;
if(vol > AK4376_AMP_VOLUME_MAX) return 14;
return (vol - AK4376_AMP_VOLUME_MIN) / AK4376_AMP_VOLUME_STEP + 1;
}
void audiohw_set_volume(int vol_l, int vol_r)
{
int amp;
int mix_l = AK4376_MIX_LCH, dig_l, sw_l;
int mix_r = AK4376_MIX_RCH, dig_r, sw_r;
if(vol_l <= AK4376_MIN_VOLUME && vol_r <= AK4376_MIN_VOLUME) {
/* Special case for full mute */
amp = AK4376_AMP_VOLUME_MUTE;
dig_l = dig_r = AK4376_DIG_VOLUME_MUTE;
sw_l = sw_r = PCM_MUTE_LEVEL;
} else {
/* Amp is a mono control -- calculate based on the loudest channel.
* The quieter channel then gets reduced more by digital controls. */
amp = round_step_up(MAX(vol_l, vol_r), AK4376_AMP_VOLUME_STEP);
amp = MIN(amp, AK4376_AMP_VOLUME_MAX);
amp = MAX(amp, AK4376_AMP_VOLUME_MIN);
/* Other controls are stereo */
calc_volumes(vol_l - amp, &mix_l, &dig_l, &sw_l);
calc_volumes(vol_r - amp, &mix_r, &dig_r, &sw_r);
}
ak4376_write(AK4376_REG_MIXER, (mix_l & 0xf) | ((mix_r & 0xf) << 4));
ak4376_write(AK4376_REG_LCH_VOLUME, dig_vol_to_hw(dig_l) | (1 << 7));
ak4376_write(AK4376_REG_RCH_VOLUME, dig_vol_to_hw(dig_r));
ak4376_write(AK4376_REG_AMP_VOLUME, amp_vol_to_hw(amp));
pcm_set_master_volume(sw_l, sw_r);
}
void audiohw_set_filter_roll_off(int val)
{
int reg = ak4376_read(AK4376_REG_FILTER);
reg &= ~0xc0;
reg |= (val & 3) << 6;
ak4376_write(AK4376_REG_FILTER, reg);
}
void audiohw_set_frequency(int fsel)
{
/* Determine master clock multiplier */
int mult = ak4376_set_mclk_freq(fsel, false);
/* Calculate clock mode for frequency. Multipliers of 32/64 are only
* for rates >= 256 KHz which are not supported by Rockbox, so they
* are commented out -- but they're in the correct place. */
int clock_mode = ak4376_fsel_to_hw[fsel];
switch(mult) {
/* case 32: */
case 256:
break;
/* case 64: */
case 512:
clock_mode |= 0x20;
break;
case 1024:
clock_mode |= 0x40;
break;
case 128:
clock_mode |= 0x60;
break;
default:
panicf("ak4376: bad master clock multiple %d", mult);
return;
}
/* Handle the DSMLP bit in the MODE_CTRL register */
int mode_ctrl = 0x00;
if(ak4376.low_mode || hw_freq_sampr[fsel] <= SAMPR_12)
mode_ctrl |= 0x40;
/* Program the new settings */
ak4376_write(AK4376_REG_CLOCK_MODE, clock_mode);
ak4376_write(AK4376_REG_MODE_CTRL, mode_ctrl);
ak4376_write(AK4376_REG_PWR3, ak4376.low_mode ? 0x11 : 0x01);
/* Enable the master clock */
ak4376_set_mclk_freq(fsel, true);
/* Remember the frequency */
ak4376.fsel = fsel;
}
void audiohw_set_power_mode(int mode)
{
/* This is handled via audiohw_set_frequency() since changing LPMODE
* bit requires power-down/power-up & changing other bits as well */
if(ak4376.low_mode != mode) {
ak4376.low_mode = mode;
audiohw_set_frequency(ak4376.fsel);
}
}

419
firmware/drivers/axp173.c Normal file
View file

@ -0,0 +1,419 @@
/***************************************************************************
* __________ __ ___.
* 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 "axp173.h"
#include "power.h"
#include "i2c-async.h"
/* Headers for the debug menu */
#ifndef BOOTLOADER
# include "action.h"
# include "list.h"
# include <stdio.h>
#endif
static const struct axp173_adc_info {
uint8_t reg;
uint8_t en_reg;
uint8_t en_bit;
} axp173_adc_info[NUM_ADC_CHANNELS] = {
{0x56, 0x82, 5}, /* ACIN_VOLTAGE */
{0x58, 0x82, 4}, /* ACIN_CURRENT */
{0x5a, 0x82, 3}, /* VBUS_VOLTAGE */
{0x5c, 0x82, 2}, /* VBUS_CURRENT */
{0x5e, 0x83, 7}, /* INTERNAL_TEMP */
{0x62, 0x82, 1}, /* TS_INPUT */
{0x78, 0x82, 7}, /* BATTERY_VOLTAGE */
{0x7a, 0x82, 6}, /* CHARGE_CURRENT */
{0x7c, 0x82, 6}, /* DISCHARGE_CURRENT */
{0x7e, 0x82, 1}, /* APS_VOLTAGE */
{0x70, 0xff, 0}, /* BATTERY_POWER */
};
static struct axp173 {
int adc_enable;
} axp173;
static void axp173_init_enabled_adcs(void)
{
axp173.adc_enable = 0;
/* Read enabled ADCs from the hardware */
uint8_t regs[2];
int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
if(rc != I2C_STATUS_OK)
return;
/* Parse registers to set ADC enable bits */
const struct axp173_adc_info* info = axp173_adc_info;
for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
if(info[i].en_reg == 0xff)
continue;
if(regs[info[i].en_reg - 0x82] & info[i].en_bit)
axp173.adc_enable |= 1 << i;
}
/* Handle battery power ADC */
if((axp173.adc_enable & (1 << ADC_BATTERY_VOLTAGE)) &&
(axp173.adc_enable & (1 << ADC_DISCHARGE_CURRENT))) {
axp173.adc_enable |= (1 << ADC_BATTERY_POWER);
}
}
void axp173_init(void)
{
axp173_init_enabled_adcs();
/* We need discharge current ADC to reliably poll for a full battery */
int bits = axp173.adc_enable;
bits |= (1 << ADC_DISCHARGE_CURRENT);
axp173_adc_set_enabled(bits);
}
/* TODO: this can STILL indicate some false positives! */
int axp173_battery_status(void)
{
int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
if(r >= 0) {
/* Charging bit indicates we're currently charging */
if((r & 0x04) != 0)
return AXP173_BATT_CHARGING;
/* Not plugged in means we're discharging */
if((r & 0xf0) == 0)
return AXP173_BATT_DISCHARGING;
} else {
/* Report discharging if we can't find out power status */
return AXP173_BATT_DISCHARGING;
}
/* If the battery is full and not in use, the charging bit will be 0,
* there will be an external power source, AND the discharge current
* will be zero. Seems to rule out all false positives. */
int d = axp173_adc_read_raw(ADC_DISCHARGE_CURRENT);
if(d == 0)
return AXP173_BATT_FULL;
return AXP173_BATT_DISCHARGING;
}
int axp173_input_status(void)
{
#ifdef HAVE_BATTERY_SWITCH
int input_status = 0;
#else
int input_status = AXP173_INPUT_BATTERY;
#endif
int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x00);
if(r < 0)
return input_status;
/* Check for AC input */
if(r & 0x80)
input_status |= AXP173_INPUT_AC;
/* Only report USB if ACIN and VBUS are not shorted */
if((r & 0x20) != 0 && (r & 0x02) == 0)
input_status |= AXP173_INPUT_USB;
#ifdef HAVE_BATTERY_SWITCH
/* Check for battery presence if target defines it as removable */
r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x01);
if(r >= 0 && (r & 0x20) != 0)
input_status |= AXP173_INPUT_BATTERY;
#endif
return input_status;
}
int axp173_adc_read(int adc)
{
int value = axp173_adc_read_raw(adc);
if(value == INT_MIN)
return INT_MIN;
return axp173_adc_conv_raw(adc, value);
}
int axp173_adc_read_raw(int adc)
{
/* Don't give a reading if the ADC is not enabled */
if((axp173.adc_enable & (1 << adc)) == 0)
return INT_MIN;
/* Read the ADC */
uint8_t buf[3];
int count = (adc == ADC_BATTERY_POWER) ? 3 : 2;
uint8_t reg = axp173_adc_info[adc].reg;
int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, reg, count, &buf[0]);
if(rc != I2C_STATUS_OK)
return INT_MIN;
/* Parse the value */
if(adc == ADC_BATTERY_POWER)
return (buf[0] << 16) | (buf[1] << 8) | buf[2];
else if(adc == ADC_CHARGE_CURRENT || adc == ADC_DISCHARGE_CURRENT)
return (buf[0] << 5) | (buf[1] & 0x1f);
else
return (buf[0] << 4) | (buf[1] & 0xf);
}
int axp173_adc_conv_raw(int adc, int value)
{
switch(adc) {
case ADC_ACIN_VOLTAGE:
case ADC_VBUS_VOLTAGE:
/* 0 mV ... 6.9615 mV, step 1.7 mV */
return value * 17 / 10;
case ADC_ACIN_CURRENT:
/* 0 mA ... 2.5594 A, step 0.625 mA */
return value * 5 / 8;
case ADC_VBUS_CURRENT:
/* 0 mA ... 1.5356 A, step 0.375 mA */
return value * 3 / 8;
case ADC_INTERNAL_TEMP:
/* -144.7 C ... 264.8 C, step 0.1 C */
return value - 1447;
case ADC_TS_INPUT:
/* 0 mV ... 3.276 V, step 0.8 mV */
return value * 4 / 5;
case ADC_BATTERY_VOLTAGE:
/* 0 mV ... 4.5045 V, step 1.1 mV */
return value * 11 / 10;
case ADC_CHARGE_CURRENT:
case ADC_DISCHARGE_CURRENT:
/* 0 mA to 4.095 A, step 0.5 mA */
return value / 2;
case ADC_APS_VOLTAGE:
/* 0 mV to 5.733 V, step 1.4 mV */
return value * 7 / 5;
case ADC_BATTERY_POWER:
/* 0 uW to 23.6404 W, step 0.55 uW */
return value * 11 / 20;
default:
/* Shouldn't happen */
return INT_MIN;
}
}
int axp173_adc_get_enabled(void)
{
return axp173.adc_enable;
}
void axp173_adc_set_enabled(int adc_bits)
{
/* Ignore no-op */
if(adc_bits == axp173.adc_enable)
return;
/* Compute the new register values */
const struct axp173_adc_info* info = axp173_adc_info;
uint8_t regs[2] = {0, 0};
for(int i = 0; i < NUM_ADC_CHANNELS; ++i) {
if(info[i].en_reg == 0xff)
continue;
if(adc_bits & (1 << i))
regs[info[i].en_reg - 0x82] |= 1 << info[i].en_bit;
}
/* These ADCs share an enable bit */
if(adc_bits & ((1 << ADC_CHARGE_CURRENT)|(1 << ADC_DISCHARGE_CURRENT))) {
adc_bits |= (1 << ADC_CHARGE_CURRENT);
adc_bits |= (1 << ADC_DISCHARGE_CURRENT);
}
/* Enable required bits for battery power ADC */
if(adc_bits & (1 << ADC_BATTERY_POWER)) {
regs[0] |= 1 << info[ADC_DISCHARGE_CURRENT].en_bit;
regs[0] |= 1 << info[ADC_BATTERY_VOLTAGE].en_bit;
}
/* Update the configuration */
i2c_reg_write(AXP173_BUS, AXP173_ADDR, 0x82, 2, &regs[0]);
axp173.adc_enable = adc_bits;
}
int axp173_adc_get_rate(void)
{
int r = i2c_reg_read1(AXP173_BUS, AXP173_ADDR, 0x84);
if(r < 0)
return AXP173_ADC_RATE_100HZ; /* an arbitrary value */
return (r >> 6) & 3;
}
void axp173_adc_set_rate(int rate)
{
i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x84,
0xc0, (rate & 3) << 6, NULL);
}
static uint32_t axp173_cc_parse(const uint8_t* buf)
{
return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
void axp173_cc_read(uint32_t* charge, uint32_t* discharge)
{
uint8_t buf[8];
int rc = i2c_reg_read(AXP173_BUS, AXP173_ADDR, 0xb0, 8, &buf[0]);
if(rc != I2C_STATUS_OK) {
if(charge)
*charge = 0;
if(discharge)
*discharge = 0;
return;
}
if(charge)
*charge = axp173_cc_parse(&buf[0]);
if(discharge)
*discharge = axp173_cc_parse(&buf[4]);
}
void axp173_cc_clear(void)
{
i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 5, 1, NULL);
}
void axp173_cc_enable(bool en)
{
i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0xb8, 7, en ? 1 : 0, NULL);
}
#ifndef BOOTLOADER
#define AXP173_DEBUG_BATTERY_STATUS 0
#define AXP173_DEBUG_INPUT_STATUS 1
#define AXP173_DEBUG_ADC_RATE 2
#define AXP173_DEBUG_FIRST_ADC 3
#define AXP173_DEBUG_ENTRIES (AXP173_DEBUG_FIRST_ADC + NUM_ADC_CHANNELS)
static int axp173_debug_menu_cb(int action, struct gui_synclist* lists)
{
(void)lists;
if(action == ACTION_NONE)
action = ACTION_REDRAW;
return action;
}
static const char* axp173_debug_menu_get_name(int item, void* data,
char* buf, size_t buflen)
{
(void)data;
static const char* const adc_names[] = {
"V_acin", "I_acin", "V_vbus", "I_vbus", "T_int",
"V_ts", "V_batt", "I_chrg", "I_dchg", "V_aps", "P_batt"
};
static const char* const adc_units[] = {
"mV", "mA", "mV", "mA", "C", "mV", "mV", "mA", "mA", "mV", "uW",
};
int adc = item - AXP173_DEBUG_FIRST_ADC;
if(item >= AXP173_DEBUG_FIRST_ADC && adc < NUM_ADC_CHANNELS) {
int raw_value = axp173_adc_read_raw(adc);
if(raw_value == INT_MIN) {
snprintf(buf, buflen, "%s: [Disabled]", adc_names[adc]);
return buf;
}
int value = axp173_adc_conv_raw(adc, raw_value);
if(adc == ADC_INTERNAL_TEMP) {
snprintf(buf, buflen, "%s: %d.%d %s", adc_names[adc],
value/10, value%10, adc_units[adc]);
} else {
snprintf(buf, buflen, "%s: %d %s", adc_names[adc],
value, adc_units[adc]);
}
return buf;
}
switch(item) {
case AXP173_DEBUG_BATTERY_STATUS: {
switch(axp173_battery_status()) {
case AXP173_BATT_FULL:
return "Battery: Full";
case AXP173_BATT_CHARGING:
return "Battery: Charging";
case AXP173_BATT_DISCHARGING:
return "Battery: Discharging";
default:
return "Battery: Unknown";
}
} break;
case AXP173_DEBUG_INPUT_STATUS: {
int s = axp173_input_status();
const char* ac = (s & AXP173_INPUT_AC) ? " AC" : "";
const char* usb = (s & AXP173_INPUT_USB) ? " USB" : "";
const char* batt = (s & AXP173_INPUT_BATTERY) ? " Battery" : "";
snprintf(buf, buflen, "Inputs:%s%s%s", ac, usb, batt);
return buf;
} break;
case AXP173_DEBUG_ADC_RATE: {
int rate = 25 << axp173_adc_get_rate();
snprintf(buf, buflen, "ADC sample rate: %d Hz", rate);
return buf;
} break;
default:
return "---";
}
}
bool axp173_debug_menu(void)
{
struct simplelist_info info;
simplelist_info_init(&info, "AXP173 debug", AXP173_DEBUG_ENTRIES, NULL);
info.action_callback = axp173_debug_menu_cb;
info.get_name = axp173_debug_menu_get_name;
return simplelist_show_list(&info);
}
#endif /* !BOOTLOADER */
/* This is basically the only valid implementation, so define it here */
unsigned int power_input_status(void)
{
unsigned int state = 0;
int input_status = axp173_input_status();
if(input_status & AXP173_INPUT_AC)
state |= POWER_INPUT_MAIN_CHARGER;
if(input_status & AXP173_INPUT_USB)
state |= POWER_INPUT_USB_CHARGER;
#ifdef HAVE_BATTERY_SWITCH
if(input_status & AXP173_INPUT_BATTERY)
state |= POWER_INPUT_BATTERY;
#endif
return state;
}

View file

@ -0,0 +1,104 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* Based mainly on rtc_jz4760.c,
* Copyright (C) 2016 by Roman Stolyarov
*
* 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 "rtc.h"
#include "x1000/rtc.h"
#include <stdint.h>
/* 4 byte magic number 'RTCV' */
#define RTCV 0x52544356
/* expected RTC clock frequency */
#define NC1HZ_EXPECTED (32768 - 1)
static void rtc_write_reg(uint32_t addr, uint32_t value)
{
while(jz_readf(RTC_CR, WRDY) == 0);
REG_RTC_WENR = 0xa55a;
while(jz_readf(RTC_WENR, WEN) == 0);
while(jz_readf(RTC_CR, WRDY) == 0);
(*(volatile uint32_t*)addr) = value;
while(jz_readf(RTC_CR, WRDY) == 0);
}
void rtc_init(void)
{
/* Check if we totally lost power and need to reset the RTC */
if(REG_RTC_HSPR != RTCV || jz_readf(RTC_GR, NC1HZ) != NC1HZ_EXPECTED) {
rtc_write_reg(JA_RTC_GR, NC1HZ_EXPECTED);
rtc_write_reg(JA_RTC_HWFCR, 3200);
rtc_write_reg(JA_RTC_HRCR, 2048);
rtc_write_reg(JA_RTC_SR, 1546300800); /* 01/01/2019 */
rtc_write_reg(JA_RTC_CR, 1);
rtc_write_reg(JA_RTC_HSPR, RTCV);
}
rtc_write_reg(JA_RTC_HWRSR, 0);
}
int rtc_read_datetime(struct tm* tm)
{
time_t time = REG_RTC_SR;
gmtime_r(&time, tm);
return 1;
}
int rtc_write_datetime(const struct tm* tm)
{
time_t time = mktime((struct tm*)tm);
rtc_write_reg(JA_RTC_SR, time);
return 1;
}
#ifdef HAVE_RTC_ALARM
/* TODO: implement the RTC alarm */
void rtc_set_alarm(int h, int m)
{
(void)h;
(void)m;
}
void rtc_get_alarm(int* h, int* m)
{
(void)h;
(void)m;
}
void rtc_enable_alarm(bool enable)
{
(void)enable;
}
bool rtc_check_alarm_started(bool release_alarm)
{
(void)release_alarm;
return false;
}
bool rtc_check_alarm_flag(void)
{
return false;
}
#endif

152
firmware/export/ak4376.h Normal file
View file

@ -0,0 +1,152 @@
/***************************************************************************
* __________ __ ___.
* 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 __AK4376_H__
#define __AK4376_H__
#define AUDIOHW_CAPS (FILTER_ROLL_OFF_CAP|POWER_MODE_CAP)
#define AUDIOHW_HAVE_SHORT2_ROLL_OFF
#define AK4376_MIN_VOLUME (-890)
#define AK4376_MAX_VOLUME 150
AUDIOHW_SETTING(VOLUME, "dB", 1, 5, AK4376_MIN_VOLUME, AK4376_MAX_VOLUME, -200)
AUDIOHW_SETTING(FILTER_ROLL_OFF, "", 0, 1, 0, 3, 0)
AUDIOHW_SETTING(POWER_MODE, "", 0, 1, 0, 1, 0)
/* Register addresses */
#define AK4376_REG_PWR1 0x00
#define AK4376_REG_PWR2 0x01
#define AK4376_REG_PWR3 0x02
#define AK4376_REG_PWR4 0x03
#define AK4376_REG_OUTPUT_MODE 0x04
#define AK4376_REG_CLOCK_MODE 0x05
#define AK4376_REG_FILTER 0x06
#define AK4376_REG_MIXER 0x07
#define AK4376_REG_LCH_VOLUME 0x0b
#define AK4376_REG_RCH_VOLUME 0x0c
#define AK4376_REG_AMP_VOLUME 0x0d
#define AK4376_REG_PLL_CLK_SRC 0x0e
#define AK4376_REG_PLL_REF_DIV1 0x0f
#define AK4376_REG_PLL_REF_DIV2 0x10
#define AK4376_REG_PLL_FB_DIV1 0x11
#define AK4376_REG_PLL_FB_DIV2 0x12
#define AK4376_REG_DAC_CLK_SRC 0x13
#define AK4376_REG_DAC_CLK_DIV 0x14
#define AK4376_REG_AUDIO_IF_FMT 0x15
#define AK4376_REG_CHIP_ID 0x21
#define AK4376_REG_MODE_CTRL 0x24
#define AK4376_REG_ADJUST1 0x26
#define AK4376_REG_ADJUST2 0x2a
#define AK4376_NUM_REGS 0x2b
/* Mixer controls, simply OR them together.
* LCH = add LCH signal to output
* RCH = add RCH signal to output
* HALF = multiply output by 1/2
* INVERT = invert the output after everything else
*/
#define AK4376_MIX_MUTE 0
#define AK4376_MIX_LCH 1
#define AK4376_MIX_RCH 2
#define AK4376_MIX_HALF 4
#define AK4376_MIX_INVERT 8
/* Min/max digital volumes in units of dB/10 */
#define AK4376_DIG_VOLUME_MIN (-120)
#define AK4376_DIG_VOLUME_MAX 30
#define AK4376_DIG_VOLUME_STEP 5
#define AK4376_DIG_VOLUME_MUTE (AK4376_DIG_VOLUME_MIN - 1)
/* Min/max headphone amp volumes in units of dB/10 */
#define AK4376_AMP_VOLUME_MIN (-200)
#define AK4376_AMP_VOLUME_MAX 60
#define AK4376_AMP_VOLUME_STEP 20
#define AK4376_AMP_VOLUME_MUTE (AK4376_AMP_VOLUME_MIN - 1)
/* Digital filters */
#define AK4376_FILTER_SHARP 0
#define AK4376_FILTER_SLOW 1
#define AK4376_FILTER_SHORT_SHARP 2
#define AK4376_FILTER_SHORT_SLOW 3
/* Frequency selection */
#define AK4376_FS_8 0
#define AK4376_FS_11 1
#define AK4376_FS_12 2
#define AK4376_FS_16 4
#define AK4376_FS_22 5
#define AK4376_FS_24 6
#define AK4376_FS_32 8
#define AK4376_FS_44 9
#define AK4376_FS_48 10
#define AK4376_FS_64 12
#define AK4376_FS_88 13
#define AK4376_FS_96 14
#define AK4376_FS_176 17
#define AK4376_FS_192 18
/* Functions to power on / off the DAC which should be called from
* the target's audiohw_init() / audiohw_close() implementation.
*/
extern void ak4376_init(void);
extern void ak4376_close(void);
/* Register read/write. Cached to avoid redundant reads/writes. */
extern void ak4376_write(int reg, int value);
extern int ak4376_read(int reg);
/* Target-specific function to set the PDN pin level. */
extern void ak4376_set_pdn_pin(int level);
/* Target-specific function to control the external master clock frequency.
* This is called by the ak4376's audiohw implementation when switching to
* or from a frequency that is configured to use this clock source.
*
* - hw_freq is the new sample rate -- one of the HW_FREQ_XX constants.
* - enabled is true if clock should be output, false if not.
*
* The return value is the master clock rate as a multiple of the sampling
* frequency. The allowed multiples depend on the sampling frequency, shown
* in the table below.
*
* +-----------+------------------------+
* | frequency | master clock rate |
* +-----------+------------------------+
* | 8 - 24 | 256fs / 512fs / 1024fs |
* | 32 - 48 | 256fs / 512fs |
* | 64 - 96 | 256fs |
* | 128 - 192 | 128fs |
* +-----------+------------------------+
*
* For example, at 48 KHz you could return either 256 or 512 depending on
* the rate you decided to actually use.
*
* You need to return a valid master multiplier for supported frequencies
* even when enabled = false, since the driver needs to know the multiplier
* _before_ enabling the clock.
*
* For unsupported frequencies you don't need to return a valid master
* multiplier, because the DAC doesn't need the return value in such cases.
*/
extern int ak4376_set_mclk_freq(int hw_freq, bool enabled);
#endif /* __AK4376_H__ */

View file

@ -194,6 +194,8 @@ struct sound_settings_info
#include "jz4740-codec.h"
#elif defined(HAVE_AK4537)
#include "ak4537.h"
#elif defined(HAVE_AK4376)
#include "ak4376.h"
#elif defined(HAVE_RK27XX_CODEC)
#include "rk27xx_codec.h"
#elif defined(HAVE_AIC3X)

94
firmware/export/axp173.h Normal file
View file

@ -0,0 +1,94 @@
/***************************************************************************
* __________ __ ___.
* 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 __AXP173_H__
#define __AXP173_H__
#include <stdbool.h>
#include <stdint.h>
#define ADC_ACIN_VOLTAGE 0
#define ADC_ACIN_CURRENT 1
#define ADC_VBUS_VOLTAGE 2
#define ADC_VBUS_CURRENT 3
#define ADC_INTERNAL_TEMP 4
#define ADC_TS_INPUT 5
#define ADC_BATTERY_VOLTAGE 6
#define ADC_CHARGE_CURRENT 7
#define ADC_DISCHARGE_CURRENT 8
#define ADC_APS_VOLTAGE 9
#define ADC_BATTERY_POWER 10
#define NUM_ADC_CHANNELS 11
/* ADC sampling rates */
#define AXP173_ADC_RATE_25HZ 0
#define AXP173_ADC_RATE_50HZ 1
#define AXP173_ADC_RATE_100HZ 2
#define AXP173_ADC_RATE_200HZ 3
/* Return values of axp173_battery_status() */
#define AXP173_BATT_DISCHARGING 0
#define AXP173_BATT_CHARGING 1
#define AXP173_BATT_FULL 2
/* Bits returned by axp173_input_status() */
#define AXP173_INPUT_AC (1 << 0)
#define AXP173_INPUT_USB (1 << 1)
#define AXP173_INPUT_BATTERY (1 << 2)
#define AXP173_INPUT_EXTERNAL (AXP173_INPUT_AC|AXP173_INPUT_USB)
/* Must be called from power_init() to initialize the driver state */
extern void axp173_init(void);
/* Basic battery and power supply status */
extern int axp173_battery_status(void);
extern int axp173_input_status(void);
/* ADC access -- ADCs which are not enabled will return INT_MIN if read.
* The output of axp173_adc_read() is normalized to appropriate units:
*
* - for voltages, the scale is millivolts
* - for currents, the scale is milliamps
* - for temperatures, the scale is tenths of a degree Celsius
* - for power, the scale is microwatts
*
* See the comment in axp173_adc_conv_raw() for raw value precision/scale.
*/
extern int axp173_adc_read(int adc);
extern int axp173_adc_read_raw(int adc);
extern int axp173_adc_conv_raw(int adc, int value);
extern int axp173_adc_get_enabled(void);
extern void axp173_adc_set_enabled(int adc_bits);
extern int axp173_adc_get_rate(void);
extern void axp173_adc_set_rate(int rate);
/* - axp173_cc_read() reads the coulomb counters
* - axp173_cc_clear() resets both counters to zero
* - axp173_cc_enable() will stop/start the counters running
*/
extern void axp173_cc_read(uint32_t* charge, uint32_t* discharge);
extern void axp173_cc_clear(void);
extern void axp173_cc_enable(bool en);
/* Debug menu */
extern bool axp173_debug_menu(void);
#endif /* __AXP173_H__ */

View file

@ -82,6 +82,7 @@
#define AS3525v2 35252
#define IMX233 233
#define RK27XX 2700
#define X1000 1000
/* platforms
* bit fields to allow PLATFORM_HOSTED to be OR'ed e.g. with a
@ -165,6 +166,7 @@
#define XDUOO_X20_PAD 70
#define FIIO_M3K_LINUX_PAD 71
#define EROSQ_PAD 72
#define FIIO_M3K_PAD 73
/* CONFIG_REMOTE_KEYPAD */
#define H100_REMOTE 1
@ -281,6 +283,7 @@
#define LCD_IHIFI770 66 /* as used by IHIFI 770 */
#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 */
/* LCD_PIXELFORMAT */
#define HORIZONTAL_PACKING 1
@ -320,6 +323,7 @@ Lyre prototype 1 */
#define I2C_S5L8702 16 /* Same as S5L8700, but with two channels */
#define I2C_IMX233 17
#define I2C_RK27XX 18
#define I2C_X1000 19
/* CONFIG_LED */
#define LED_REAL 1 /* SW controlled LED (Archos recorders, player) */
@ -356,6 +360,7 @@ Lyre prototype 1 */
#define RTC_IMX233 20
#define RTC_STM41T62 21 /* ST M41T62 */
#define RTC_JZ4760 22 /* Ingenic Jz4760 */
#define RTC_X1000 23 /* Ingenic X1000 */
/* USB On-the-go */
#define USBOTG_M66591 6591 /* M:Robe 500 */
@ -605,6 +610,8 @@ Lyre prototype 1 */
#include "config/xduoox20.h"
#elif defined(FIIO_M3K_LINUX)
#include "config/fiiom3klinux.h"
#elif defined(FIIO_M3K)
#include "config/fiiom3k.h"
#elif defined(EROS_Q)
#include "config/aigoerosq.h"
#else

View file

@ -0,0 +1,116 @@
/* RoLo-related defines */
#define MODEL_NAME "FiiO M3K"
#define MODEL_NUMBER 114
#define BOOTFILE_EXT "m3k"
#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 TIMER_FREQ X1000_EXCLK_FREQ
#define CPU_FREQ 1008000000
#define CPUFREQ_MAX CPU_FREQ
/* TODO: figure out if this does in fact affect power consumption. */
#define CPUFREQ_DEFAULT (CPUFREQ_MAX/4)
#define CPUFREQ_NORMAL (CPUFREQ_MAX/4)
#define HAVE_ADJUSTABLE_CPU_FREQ
#define HAVE_GUI_BOOST
/* 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_FIIOM3K
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#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
/* Backlight defines */
#define HAVE_BACKLIGHT
#define HAVE_BACKLIGHT_BRIGHTNESS
#define HAVE_BUTTON_LIGHT
#define HAVE_BUTTONLIGHT_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_AK4376
#define HAVE_SW_TONE_CONTROLS
#define HAVE_SW_VOLUME_CONTROL
/* TODO: Need to implement recording */
/* Button defines */
#define CONFIG_KEYPAD FIIO_M3K_PAD
#define HAVE_HEADPHONE_DETECTION
#define HAVE_TOUCHPAD
#define HAVE_TOUCHPAD_SENSITIVITY_SETTING
#define MIN_TOUCHPAD_SENSITIVITY_SETTING (-25)
#define MAX_TOUCHPAD_SENSITIVITY_SETTING (25)
#define DEFAULT_TOUCHPAD_SENSITIVITY_SETTING (0)
#define HAVE_SCROLLWHEEL
/* #define HAVE_WHEEL_ACCELERATION */
/* #define WHEEL_ACCELERATION */
/* #define WHEEL_ACCEL_START */
/* 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 HAVE_AXP173
#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE
#define CONFIG_CHARGING CHARGING_MONITOR
#define HAVE_SW_POWEROFF
#define HAVE_POWEROFF_WHILE_CHARGING
/* 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 is still TODO. */
#define USB_NONE
/* 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

View file

@ -0,0 +1,31 @@
/***************************************************************************
* __________ __ ___.
* 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 _INSTALLER_H_
#define _INSTALLER_H_
/* Provisional interface for installing/dumping a bootloader */
extern int install_bootloader(const char* path);
extern int dump_bootloader(const char* path);
extern const char* installer_strerror(int rc);
#endif /* _INSTALLER_H_ */

View file

@ -131,7 +131,8 @@ static int battery_type = 0;
/* Power history: power_history[0] is the newest sample */
unsigned short power_history[POWER_HISTORY_LEN] = {0};
#if (CONFIG_CPU == JZ4732) || (CONFIG_CPU == JZ4760B) || (CONFIG_PLATFORM & PLATFORM_HOSTED)
#if (CONFIG_CPU == JZ4732) || (CONFIG_CPU == JZ4760B) || \
(CONFIG_CPU == X1000) || (CONFIG_PLATFORM & PLATFORM_HOSTED)
static char power_stack[DEFAULT_STACK_SIZE + POWERMGMT_DEBUG_STACK];
#else
static char power_stack[DEFAULT_STACK_SIZE/2 + POWERMGMT_DEBUG_STACK];

View file

@ -0,0 +1,119 @@
/***************************************************************************
* __________ __ ___.
* 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 "aic-x1000.h"
#include "gpio-x1000.h"
#include "x1000/aic.h"
#include "x1000/cpm.h"
/* Given a rational number m/n < 1, find its representation as a continued
* fraction [0; a1, a2, a3, ..., a_k]. At most "cnt" terms are calculated
* and written out to "buf". Returns the number of terms written; the result
* is complete if this value is less than "cnt", and may be incomplete if it
* is equal to "cnt". (Note the leading zero term is not written to "buf".)
*/
static unsigned cf_derive(unsigned m, unsigned n, unsigned* buf, unsigned cnt)
{
unsigned wrote = 0;
unsigned a = m / n;
while(cnt--) {
unsigned tmp = n;
n = m - n * a;
if(n == 0)
break;
m = tmp;
a = m / n;
*buf++ = a;
wrote++;
}
return wrote;
}
/* Given a finite continued fraction [0; buf[0], buf[1], ..., buf[count-1]],
* calculate the rational number m/n which it represents. Returns m and n.
* If count is zero, then m and n are undefined.
*/
static void cf_expand(const unsigned* buf, unsigned count,
unsigned* m, unsigned* n)
{
if(count == 0)
return;
unsigned i = count - 1;
unsigned mx = 1, nx = buf[i];
while(i--) {
unsigned tmp = nx;
nx = mx + buf[i] * nx;
mx = tmp;
}
*m = mx;
*n = nx;
}
int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult)
{
/* get the input clock rate */
uint32_t src_freq = clk_get(clksrc);
/* reject invalid parameters */
if(mult % 64 != 0)
return -1;
if(clksrc == X1000_EXCLK_FREQ) {
if(mult != 0)
return -1;
jz_writef(AIC_I2SCR, STPBK(1));
jz_writef(CPM_I2SCDR, CS(0), CE(0));
REG_AIC_I2SDIV = X1000_EXCLK_FREQ / 64 / fs;
} else {
if(mult == 0)
return -1;
if(fs*mult > src_freq)
return -1;
/* calculate best rational approximation that fits our constraints */
unsigned m = 0, n = 0;
unsigned buf[16];
unsigned cnt = cf_derive(fs*mult, src_freq, &buf[0], 16);
do {
cf_expand(&buf[0], cnt, &m, &n);
cnt -= 1;
} while(cnt > 0 && (m > 512 || n > 8192) && (n >= 2*m));
/* wrong values */
if(cnt == 0 || n == 0 || m == 0)
return -1;
jz_writef(AIC_I2SCR, STPBK(1));
jz_writef(CPM_I2SCDR, PCS(clksrc == X1000_CLK_MPLL ? 1 : 0),
CS(1), CE(1), DIV_M(m), DIV_N(n));
jz_write(CPM_I2SCDR1, REG_CPM_I2SCDR1);
REG_AIC_I2SDIV = (mult / 64) - 1;
}
jz_writef(AIC_I2SCR, STPBK(0));
return 0;
}

View file

@ -0,0 +1,46 @@
/***************************************************************************
* __________ __ ___.
* 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 __AIC_X1000_H__
#define __AIC_X1000_H__
#include "clk-x1000.h"
#include <stdbool.h>
/* Set frequency of I2S master clock supplied by AIC. Has no use if an
* external DAC is supplying the master clock. Must be called with the
* bit clock disabled.
*
* - clksrc can be one of EXCLK, SCLK_A, MPLL.
* - This function does not modify PLL settings. It's the caller's job
* to ensure the PLL is configured and runing.
* - fs is the audio sampling frequency (8 KHz - 192 KHz)
* - mult is multiplied by fs to get the master clock rate.
* - mult must be a multiple of 64 due to AIC bit clock requirements.
* - Note: EXCLK bypasses the decimal divider so it is not very flexible.
* If using EXCLK you must set mult=0. If EXCLK is not a multiple of
* the bit clock (= 64*fs), then the clock rate will be inaccurate.
*
* Returns zero on success and nonzero if the frequency is not achievable.
*/
extern int aic_i2s_set_mclk(x1000_clk_t clksrc, unsigned fs, unsigned mult);
#endif /* __AIC_X1000_H__ */

View file

@ -0,0 +1,119 @@
#include "config.h"
OUTPUT_FORMAT("elf32-littlemips")
OUTPUT_ARCH(MIPS)
ENTRY(_start)
STARTUP(target/mips/ingenic_x1000/crt0.o)
/* Stub area is used for loading new firmware via RoLo */
#define STUBSIZE 0x4000
#define SDRAM_ORIG 0x80000000
/* IRAM contains stub, DRAM contains main app */
#define IRAMORIG SDRAM_ORIG
#define IRAMSIZE STUBSIZE
#define DRAMORIG (SDRAM_ORIG + STUBSIZE)
#define DRAMSIZE (MEMORYSIZE * 0x100000 - STUBSIZE)
/* End of the audio buffer, where the codec buffer starts */
#define ENDAUDIOADDR (DRAMORIG + DRAMSIZE - PLUGIN_BUFFER_SIZE - CODEC_SIZE)
/* Where the codec buffer ends, and the plugin buffer starts */
#define ENDCODECADDR (ENDAUDIOADDR + CODEC_SIZE)
MEMORY
{
IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE
DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE
}
SECTIONS
{
.text :
{
loadaddress = .;
_loadaddress = .;
*(.init.text);
*(.text*);
} > DRAM
. = ALIGN(4);
.rodata :
{
*(.rodata*);
} > DRAM
. = ALIGN(4);
.data :
{
*(.data*);
*(.sdata*);
} > DRAM
.iram IRAMORIG: AT (_bssbegin)
{
_iramstart = .;
. = 0x000; /* TLB refill */
KEEP(*(.vectors.1));
. = 0x100; /* Cache error */
KEEP(*(.vectors.2));
. = 0x180; /* General exception */
KEEP(*(.vectors.3));
. = 0x200; /* Interrupt */
KEEP(*(.vectors.4));
KEEP(*(.vectors));
*(.icode);
*(.irodata);
*(.idata);
_iramend = .;
} > IRAM
_iramcopy = LOADADDR(.iram);
. = ALIGN(4);
.stack (NOLOAD) :
{
*(.stack);
stackbegin = .;
. += 0x1E00;
stackend = .;
_irqstackbegin = .;
. += 0x300;
_irqstackend = .;
} > IRAM
.bss (NOLOAD) :
{
_bssbegin = .;
*(.sbss*);
*(.bss*);
*(COMMON);
*(.scommon*);
_bssend = .;
_end = .;
} > DRAM
#ifdef BOOTLOADER
. = ALIGN(4);
loadbuffer = .;
. += 0x100000 * 4; /* Allow 4 MiB for the rockbox binary */
loadbufferend = .;
#else
.audiobuf :
{
. = ALIGN(4); /* XXX might need more alignment here */
audiobuffer = .;
} > DRAM
audiobufend = ENDAUDIOADDR;
codecbuf = ENDAUDIOADDR;
pluginbuf = ENDCODECADDR;
#endif
/DISCARD/ :
{
*(.eh_frame);
*(.rel.dyn);
}
}

View file

@ -0,0 +1,5 @@
#ifdef BOOTLOADER_SPL
# include "spl.lds"
#else
# include "app.lds"
#endif

View file

@ -0,0 +1,258 @@
/***************************************************************************
* __________ __ ___.
* 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 "x1000/cpm.h"
#include "x1000/msc.h"
#include "x1000/aic.h"
static uint32_t pll_get(uint32_t pllreg, uint32_t onbit)
{
if((pllreg & (1 << onbit)) == 0)
return 0;
/* Both PLL registers share the same layout of N/M/OD bits.
* The max multiplier is 128 and max EXCLK is 26 MHz, so the
* multiplication should fit within 32 bits without overflow.
*/
uint32_t rate = X1000_EXCLK_FREQ;
rate *= jz_vreadf(pllreg, CPM_APCR, PLLM) + 1;
rate /= jz_vreadf(pllreg, CPM_APCR, PLLN) + 1;
rate >>= jz_vreadf(pllreg, CPM_APCR, PLLOD);
return rate;
}
static uint32_t sclk_a_get(void)
{
switch(jz_readf(CPM_CCR, SEL_SRC)) {
case 1: return X1000_EXCLK_FREQ;
case 2: return clk_get(X1000_CLK_APLL);
default: return 0;
}
}
static uint32_t ccr_get(uint32_t selbit, uint32_t divbit)
{
uint32_t reg = REG_CPM_CCR;
uint32_t sel = (reg >> selbit) & 0x3;
uint32_t div = (reg >> divbit) & 0xf;
switch(sel) {
case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
default: return 0;
}
}
static uint32_t ddr_get(void)
{
uint32_t reg = REG_CPM_DDRCDR;
uint32_t div = jz_vreadf(reg, CPM_DDRCDR, CLKDIV);
switch(jz_vreadf(reg, CPM_DDRCDR, CLKSRC)) {
case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1);
case 2: return clk_get(X1000_CLK_MPLL) / (div + 1);
default: return 0;
}
}
static uint32_t lcd_get(void)
{
if(jz_readf(CPM_CLKGR, LCD))
return 0;
uint32_t reg = REG_CPM_LPCDR;
uint32_t rate;
switch(jz_vreadf(reg, CPM_LPCDR, CLKSRC)) {
case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
case 1: rate = clk_get(X1000_CLK_MPLL); break;
default: return 0;
}
rate /= jz_vreadf(reg, CPM_LPCDR, CLKDIV) + 1;
return rate;
}
static uint32_t msc_get(int msc)
{
if((msc == 0 && jz_readf(CPM_CLKGR, MSC0)) ||
(msc == 1 && jz_readf(CPM_CLKGR, MSC1)))
return 0;
uint32_t reg = REG_CPM_MSC0CDR;
uint32_t rate;
switch(jz_vreadf(reg, CPM_MSC0CDR, CLKSRC)) {
case 0: rate = clk_get(X1000_CLK_SCLK_A); break;
case 1: rate = clk_get(X1000_CLK_MPLL); break;
default: return 0;
}
uint32_t div;
if(msc == 0)
div = jz_readf(CPM_MSC0CDR, CLKDIV);
else
div = jz_readf(CPM_MSC1CDR, CLKDIV);
rate /= 2 * (div + 1);
rate >>= REG_MSC_CLKRT(msc);
return rate;
}
static uint32_t i2s_mclk_get(void)
{
if(jz_readf(CPM_CLKGR, AIC))
return 0;
uint32_t reg = REG_CPM_I2SCDR;
unsigned long long rate;
if(jz_vreadf(reg, CPM_I2SCDR, CS) == 0)
rate = X1000_EXCLK_FREQ;
else {
if(jz_vreadf(reg, CPM_I2SCDR, PCS) == 0)
rate = clk_get(X1000_CLK_SCLK_A);
else
rate = clk_get(X1000_CLK_MPLL);
rate *= jz_vreadf(reg, CPM_I2SCDR, DIV_M);
rate /= jz_vreadf(reg, CPM_I2SCDR, DIV_N);
}
/* Clamp invalid setting to 32 bits */
if(rate > 0xffffffffull)
rate = 0xffffffff;
return rate;
}
static uint32_t i2s_bclk_get(void)
{
return i2s_mclk_get() / (REG_AIC_I2SDIV + 1);
}
static uint32_t sfc_get(void)
{
if(jz_readf(CPM_CLKGR, SFC))
return 0;
uint32_t reg = REG_CPM_SSICDR;
uint32_t rate;
if(jz_vreadf(reg, CPM_SSICDR, SFC_CS) == 0)
rate = clk_get(X1000_CLK_SCLK_A);
else
rate = clk_get(X1000_CLK_MPLL);
rate /= jz_vreadf(reg, CPM_SSICDR, CLKDIV) + 1;
return rate;
}
uint32_t clk_get(x1000_clk_t clk)
{
switch(clk) {
case X1000_CLK_EXCLK: return X1000_EXCLK_FREQ;
case X1000_CLK_APLL: return pll_get(REG_CPM_APCR, BP_CPM_APCR_ON);
case X1000_CLK_MPLL: return pll_get(REG_CPM_MPCR, BP_CPM_MPCR_ON);
case X1000_CLK_SCLK_A: return sclk_a_get();
case X1000_CLK_CPU: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_CDIV);
case X1000_CLK_L2CACHE: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_L2DIV);
case X1000_CLK_AHB0: return ccr_get(BP_CPM_CCR_SEL_H0PLL, BP_CPM_CCR_H0DIV);
case X1000_CLK_AHB2: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_H2DIV);
case X1000_CLK_PCLK: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_PDIV);
case X1000_CLK_DDR: return ddr_get();
case X1000_CLK_LCD: return lcd_get();
case X1000_CLK_MSC0: return msc_get(0);
case X1000_CLK_MSC1: return msc_get(1);
case X1000_CLK_I2S_MCLK: return i2s_mclk_get();
case X1000_CLK_I2S_BCLK: return i2s_bclk_get();
case X1000_CLK_SFC: return sfc_get();
default: return 0;
}
}
const char* clk_get_name(x1000_clk_t clk)
{
switch(clk) {
#define CASE(x) case X1000_CLK_##x: return #x
CASE(EXCLK);
CASE(APLL);
CASE(MPLL);
CASE(SCLK_A);
CASE(CPU);
CASE(L2CACHE);
CASE(AHB0);
CASE(AHB2);
CASE(PCLK);
CASE(DDR);
CASE(LCD);
CASE(MSC0);
CASE(MSC1);
CASE(I2S_MCLK);
CASE(I2S_BCLK);
CASE(SFC);
#undef CASE
default:
return "NONE";
}
}
#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL)
#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX)
#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY)
void clk_set_ccr_mux(uint32_t muxbits)
{
/* Set new mux configuration */
uint32_t reg = REG_CPM_CCR;
reg &= ~CCR_MUX_BITS;
reg |= muxbits & CCR_MUX_BITS;
REG_CPM_CCR = reg;
/* Wait for mux change to complete */
while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS);
}
void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk)
{
/* Set new divider configuration */
jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1),
H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1),
CE_CPU(1), CE_AHB0(1), CE_AHB2(1));
/* Wait until divider change completes */
while(REG_CPM_CSR & CSR_DIV_BITS);
/* Disable CE bits after change */
jz_writef(CPM_CCR, CE_CPU(0), CE_AHB0(0), CE_AHB2(0));
}
void clk_set_ddr(x1000_clk_t src, uint32_t div)
{
/* Write new configuration */
jz_writef(CPM_DDRCDR, CE(1), CLKDIV(div - 1),
CLKSRC(src == X1000_CLK_MPLL ? 2 : 1));
/* Wait until mux and divider change are complete */
while(jz_readf(CPM_CSR, DDR_MUX) == 0);
while(jz_readf(CPM_DDRCDR, BUSY));
/* Disable CE bit after change */
jz_writef(CPM_DDRCDR, CE(0));
}

View file

@ -0,0 +1,82 @@
/***************************************************************************
* __________ __ ___.
* 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 __CLK_X1000_H__
#define __CLK_X1000_H__
#include <stdint.h>
#include "x1000/cpm.h"
/* Used as arguments to clk_set_ccr_mux() */
#define CLKMUX_SCLK_A(x) jz_orf(CPM_CCR, SEL_SRC_V(x))
#define CLKMUX_CPU(x) jz_orf(CPM_CCR, SEL_CPLL_V(x))
#define CLKMUX_AHB0(x) jz_orf(CPM_CCR, SEL_H0PLL_V(x))
#define CLKMUX_AHB2(x) jz_orf(CPM_CCR, SEL_H2PLL_V(x))
typedef enum x1000_clk_t {
X1000_CLK_EXCLK,
X1000_CLK_APLL,
X1000_CLK_MPLL,
X1000_CLK_SCLK_A,
X1000_CLK_CPU,
X1000_CLK_L2CACHE,
X1000_CLK_AHB0,
X1000_CLK_AHB2,
X1000_CLK_PCLK,
X1000_CLK_DDR,
X1000_CLK_LCD,
X1000_CLK_MSC0,
X1000_CLK_MSC1,
X1000_CLK_I2S_MCLK,
X1000_CLK_I2S_BCLK,
X1000_CLK_SFC,
X1000_CLK_COUNT,
} x1000_clk_t;
/* Calculate the current frequency of a clock */
extern uint32_t clk_get(x1000_clk_t clk);
/* Get the name of a clock for debug purposes */
extern const char* clk_get_name(x1000_clk_t clk);
/* Sets system clock multiplexers */
extern void clk_set_ccr_mux(uint32_t muxbits);
/* Sets system clock dividers */
extern void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk);
/* Sets DDR clock source and divider */
extern void clk_set_ddr(x1000_clk_t src, uint32_t div);
/* Returns the smallest n such that infreq/n <= outfreq */
inline uint32_t clk_calc_div(uint32_t infreq, uint32_t outfreq)
{
return (infreq + (outfreq - 1)) / outfreq;
}
/* Returns the smallest n such that (infreq >> n) <= outfreq */
inline uint32_t clk_calc_shift(uint32_t infreq, uint32_t outfreq)
{
uint32_t div = clk_calc_div(infreq, outfreq);
return __builtin_clz(div) ^ 31;
}
#endif /* __CLK_X1000_H__ */

View file

@ -0,0 +1,265 @@
/***************************************************************************
* __________ __ ___.
* 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 "config.h"
#include "mips.h"
.text
.extern main
.global _start
.set push
.set mips32
.set noreorder
.set noat
.section .init.text
_start:
/* Clear data watchpoint */
mtc0 zero, C0_WATCHLO
mtc0 zero, C0_WATCHHI
/* Set BEV, ERL, mask interrupts */
li v0, 0x40fc04
mtc0 v0, C0_Status
/* Set Cause_IV to 1 (use special interrupt vector) */
li v0, M_CauseIV
mtc0 v0, C0_Cause
/* Set CPU_MODE and BUS_MODE to 1 in CPM_OPCR (Ingenic does this) */
lui v0, 0xb000
lw v1, 0x24(v0)
ori v1, v1, 0x22
sw v1, 0x24(v0)
/* Enable kseg0 cacheability */
li v0, 3
mtc0 v0, C0_Config
nop
/* According to ingenic: "enable idx-store-data cache insn" */
li v0, 0x20000000
mtc0 v0, C0_ErrCtl
/* Cache init */
li v0, 0x80000000
ori v1, v0, 0x4000
mtc0 zero, C0_TAGLO
mtc0 zero, C0_TAGHI
_cache_loop:
cache ICIndexStTag, 0(v0)
cache DCIndexStTag, 0(v0)
addiu v0, v0, 32
bne v0, v1, _cache_loop
nop
/* Invalidate BTB */
mfc0 v0, C0_Config, 7
nop
ori v0, v0, 2
mtc0 v0, C0_Config, 7
nop
#ifndef BOOTLOADER_SPL
/* Copy IRAM from BSS to low memory. */
la t0, _iramcopy
la t1, _iramstart
la t2, _iramend
_iram_loop:
lw t3, 0(t0)
addiu t1, 4
addiu t0, 4
bne t1, t2, _iram_loop
sw t3, -4(t1)
#endif
/* Clear the BSS segment (needed to zero-initialize C static values) */
la t0, _bssbegin
la t1, _bssend
beq t0, t1, _bss_done
_bss_loop:
addiu t0, 4
bne t0, t1, _bss_loop
sw zero, -4(t0)
_bss_done:
#ifndef BOOTLOADER_SPL
/* Set stack pointer and clear the stack */
la sp, stackend
la t0, stackbegin
li t1, 0xDEADBEEF
_stack_loop:
addiu t0, 4
bne t0, sp, _stack_loop
sw t1, -4(t0)
/* Clear the IRQ stack */
la k0, _irqstackend
la t0, _irqstackbegin
_irqstack_loop:
addiu t0, 4
bne t0, k0, _irqstack_loop
sw t1, -4(t0)
#endif
/* Jump to C code */
j main
nop
#ifndef BOOTLOADER_SPL
/* Exception entry points */
.section .vectors.1, "ax", %progbits
j tlb_refill_handler
nop
.section .vectors.2, "ax", %progbits
j real_exception_handler
nop
.section .vectors.3, "ax", %progbits
j real_exception_handler
nop
.section .vectors.4, "ax", %progbits
j real_exception_handler
nop
.section .vectors, "ax", %progbits
real_exception_handler:
move k0, sp
la sp, _irqstackend
addiu sp, -0x84
sw k0, 0x80(sp)
sw ra, 0x00(sp)
sw fp, 0x04(sp)
sw gp, 0x08(sp)
sw t9, 0x0c(sp)
sw t8, 0x10(sp)
sw s7, 0x14(sp)
sw s6, 0x18(sp)
sw s5, 0x1c(sp)
sw s4, 0x20(sp)
sw s3, 0x24(sp)
sw s2, 0x28(sp)
sw s1, 0x2c(sp)
sw s0, 0x30(sp)
sw t7, 0x34(sp)
sw t6, 0x38(sp)
sw t5, 0x3c(sp)
sw t4, 0x40(sp)
sw t3, 0x44(sp)
sw t2, 0x48(sp)
sw t1, 0x4c(sp)
sw t0, 0x50(sp)
sw a3, 0x54(sp)
sw a2, 0x58(sp)
sw a1, 0x5c(sp)
sw a0, 0x60(sp)
sw v1, 0x64(sp)
sw v0, 0x68(sp)
sw $1, 0x6c(sp)
mflo k0
nop
sw k0, 0x70(sp)
mfhi k0
nop
sw k0, 0x74(sp)
mfc0 k0, C0_STATUS
nop
nop
nop
sw k0, 0x78(sp)
mfc0 k0, C0_EPC
nop
nop
nop
sw k0, 0x7c(sp)
li k1, M_CauseExcCode
mfc0 a0, C0_CAUSE
and k0, a0, k1
bnez k0, _exception
nop
jal intr_handler
nop
j _exception_return
_exception:
mfc0 a1, C0_EPC
nop
nop
nop
jal exception_handler
move a2, sp
_exception_return:
lw ra, 0x00(sp)
lw fp, 0x04(sp)
lw gp, 0x08(sp)
lw t9, 0x0c(sp)
lw t8, 0x10(sp)
lw s7, 0x14(sp)
lw s6, 0x18(sp)
lw s5, 0x1c(sp)
lw s4, 0x20(sp)
lw s3, 0x24(sp)
lw s2, 0x28(sp)
lw s1, 0x2c(sp)
lw s0, 0x30(sp)
lw t7, 0x34(sp)
lw t6, 0x38(sp)
lw t5, 0x3c(sp)
lw t4, 0x40(sp)
lw t3, 0x44(sp)
lw t2, 0x48(sp)
lw t1, 0x4c(sp)
lw t0, 0x50(sp)
lw a3, 0x54(sp)
lw a2, 0x58(sp)
lw a1, 0x5c(sp)
lw a0, 0x60(sp)
lw v1, 0x64(sp)
lw v0, 0x68(sp)
lw $1, 0x6c(sp)
lw k0, 0x70(sp)
mtlo k0
nop
lw k0, 0x74(sp)
mthi k0
nop
lw k0, 0x78(sp)
mtc0 k0, C0_STATUS
nop
nop
nop
lw k0, 0x7c(sp)
mtc0 k0, C0_EPC
nop
nop
nop
lw sp, 0x80(sp)
eret
nop
#endif
.set pop

View file

@ -0,0 +1,215 @@
/***************************************************************************
* __________ __ ___.
* 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 BOOTLOADER
#include "system.h"
#include "kernel.h"
#include "button.h"
#include "lcd.h"
#include "font.h"
#include "action.h"
#include "list.h"
#include "clk-x1000.h"
#include "gpio-x1000.h"
static bool dbg_clocks(void)
{
do {
lcd_clear_display();
int line = 0;
for(int i = 0; i < X1000_CLK_COUNT; ++i) {
uint32_t hz = clk_get(i);
uint32_t khz = hz / 1000;
uint32_t mhz = khz / 1000;
lcd_putsf(2, line++, "%8s %4u,%03u,%03u Hz", clk_get_name(i),
mhz, (khz - mhz*1000), (hz - khz*1000));
}
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
return false;
}
static void dbg_gpios_show_state(void)
{
const char portname[] = "ABCD";
for(int i = 0; i < 4; ++i)
lcd_putsf(0, i, "GPIO %c: %08x", portname[i], REG_GPIO_PIN(i));
}
static void dbg_gpios_show_config(void)
{
const char portname[] = "ABCD";
int line = 0;
for(int i = 0; i < 4; ++i) {
uint32_t intr = REG_GPIO_INT(i);
uint32_t mask = REG_GPIO_MSK(i);
uint32_t pat0 = REG_GPIO_PAT0(i);
uint32_t pat1 = REG_GPIO_PAT1(i);
lcd_putsf(0, line++, "GPIO %c", portname[i]);
lcd_putsf(2, line++, " int %08lx", intr);
lcd_putsf(2, line++, " msk %08lx", mask);
lcd_putsf(2, line++, "pat0 %08lx", pat0);
lcd_putsf(2, line++, "pat1 %08lx", pat1);
line++;
}
}
static bool dbg_gpios(void)
{
enum { STATE, CONFIG, NUM_SCREENS };
const int timeouts[NUM_SCREENS] = { 1, HZ };
int screen = STATE;
while(1) {
lcd_clear_display();
switch(screen) {
case CONFIG:
dbg_gpios_show_config();
break;
case STATE:
dbg_gpios_show_state();
break;
}
lcd_update();
switch(get_action(CONTEXT_STD, timeouts[screen])) {
case ACTION_STD_CANCEL:
return false;
case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT:
screen -= 1;
if(screen < 0)
screen = NUM_SCREENS - 1;
break;
case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT:
screen += 1;
if(screen >= NUM_SCREENS)
screen = 0;
break;
default:
break;
}
}
return false;
}
extern volatile unsigned aic_tx_underruns;
static bool dbg_audio(void)
{
do {
lcd_clear_display();
lcd_putsf(0, 0, "TX underruns: %u", aic_tx_underruns);
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
return false;
}
static bool dbg_cpuidle(void)
{
do {
lcd_clear_display();
lcd_putsf(0, 0, "CPU idle time: %d.%01d%%",
__cpu_idle_cur/10, __cpu_idle_cur%10);
lcd_putsf(0, 1, "CPU frequency: %d.%03d MHz",
FREQ/1000000, (FREQ%1000000)/1000);
lcd_update();
} while(get_action(CONTEXT_STD, HZ) != ACTION_STD_CANCEL);
return false;
}
#ifdef FIIO_M3K
extern bool dbg_fiiom3k_touchpad(void);
extern bool axp173_debug_menu(void);
#endif
/* Menu definition */
static const struct {
const char* name;
bool(*function)(void);
} menuitems[] = {
{"Clocks", &dbg_clocks},
{"GPIOs", &dbg_gpios},
{"CPU idle", &dbg_cpuidle},
{"Audio", &dbg_audio},
#ifdef FIIO_M3K
{"Touchpad", &dbg_fiiom3k_touchpad},
{"Power stats", &axp173_debug_menu},
#endif
};
static int hw_info_menu_action_cb(int btn, struct gui_synclist* lists)
{
if(btn == ACTION_STD_OK) {
int sel = gui_synclist_get_sel_pos(lists);
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
lcd_setfont(FONT_SYSFIXED);
lcd_set_foreground(LCD_WHITE);
lcd_set_background(LCD_BLACK);
if(menuitems[sel].function())
btn = SYS_USB_CONNECTED;
else
btn = ACTION_REDRAW;
lcd_setfont(FONT_UI);
FOR_NB_SCREENS(i)
viewportmanager_theme_undo(i, false);
}
return btn;
}
static const char* hw_info_menu_get_name(int item, void* data,
char* buffer, size_t buffer_len)
{
(void)buffer;
(void)buffer_len;
(void)data;
return menuitems[item].name;
}
bool dbg_hw_info(void)
{
struct simplelist_info info;
simplelist_info_init(&info, MODEL_NAME " debug menu",
ARRAYLEN(menuitems), NULL);
info.action_callback = hw_info_menu_action_cb;
info.get_name = hw_info_menu_get_name;
return simplelist_show_list(&info);
}
bool dbg_ports(void)
{
return false;
}
#endif

View file

@ -0,0 +1,91 @@
/***************************************************************************
* __________ __ ___.
* 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 "dma-x1000.h"
#include "irq-x1000.h"
#include "x1000/cpm.h"
#include "panic.h"
static dma_cb_func dma_callbacks[DMA_NUM_USED_CHANNELS];
static void dma_no_cb(int event)
{
(void)event;
panicf("Unhandled DMA channel interrupt");
}
void dma_init(void)
{
for(int i = 0; i < DMA_NUM_USED_CHANNELS; ++i)
dma_callbacks[i] = dma_no_cb;
jz_writef(CPM_CLKGR, PDMA(0));
jz_writef(DMA_CTRL, ENABLE(1), HALT(0), AR(0));
jz_writef(DMA_CTRL, FMSC(1), FSSI(1), FTSSI(1), FUART(1), FAIC(1));
system_enable_irq(IRQ_PDMA);
system_enable_irq(IRQ_PDMAD);
}
void dma_set_callback(int chn, dma_cb_func cb)
{
dma_callbacks[chn] = cb != NULL ? cb : dma_no_cb;
}
void PDMA(void)
{
/* This is called when the last descriptor completes, or if the
* channel hits an error.
*/
unsigned pending = REG_DMA_IRQP;
for(int i = 0; i < DMA_NUM_USED_CHANNELS; ++i) {
if((pending & (1 << i)) == 0)
continue;
int evt;
if(REG_DMA_CHN_CS(i) & jz_orm(DMA_CHN_CS, AR, HLT))
evt = DMA_EVENT_ERROR;
else
evt = DMA_EVENT_COMPLETE;
REG_DMA_CHN_CS(i) = 0;
dma_callbacks[i](evt);
}
/* Clear any errors and clear interrupts */
jz_writef(DMA_CTRL, HALT(0), AR(0));
REG_DMA_IRQP = 0;
}
void PDMAD(void)
{
/* Called when TIE is set on a non-final descriptor */
unsigned pending = REG_DMA_DIP;
for(int i = 0; i < DMA_NUM_USED_CHANNELS; ++i) {
if((pending & (1 << i)) == 0)
continue;
dma_callbacks[i](DMA_EVENT_INTERRUPT);
}
/* This does not operate like other clear registers */
REG_DMA_DIC &= ~pending;
}

View file

@ -0,0 +1,69 @@
/***************************************************************************
* __________ __ ___.
* 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 __DMA_X1000_H__
#define __DMA_X1000_H__
#include "x1000/dma.h"
#include "x1000/dma_chn.h"
#include <stdint.h>
/* Events passed to DMA callbacks */
#define DMA_EVENT_NONE 0 /* Not used by DMA code but can be used as
* a sentinel value to indicate "no event" */
#define DMA_EVENT_INTERRUPT 1 /* Interrupt on a non-final descriptor */
#define DMA_EVENT_COMPLETE 2 /* Completed the final descriptor */
#define DMA_EVENT_ERROR 3 /* Some kind of error occurred */
/* All DMA channels which use interrupts must be statically defined here.
* The channel numbering should be contiguous, and lower channel numbers
* will have lower interrupt latency because they're serviced first.
*
* Channels >= DMA_NUM_USED_CHANNELS will NOT have interrupts serviced!
* Due to the possibility of address error interrupts that can occur even
* if no interrupts are requested on the channel, the unallocated channels
* cannot be used safely.
*/
#define DMA_CHANNEL_AUDIO 0
#define DMA_CHANNEL_FBCOPY 1
#define DMA_NUM_USED_CHANNELS 2
struct dma_desc {
uint32_t cm; /* meaning and layout same as DMA_CHN_CM */
uint32_t sa; /* source address */
uint32_t ta; /* target address */
uint32_t tc; /* low 24 bits: transfer count
* upper 8 bits: offset to next descriptor
*/
uint32_t sd; /* same as DMA_CHN_SD */
uint32_t rt; /* request type, same as DMA_CHN_RT */
uint32_t pad0;
uint32_t pad1;
} __attribute__((aligned(32)));
typedef struct dma_desc dma_desc;
typedef void(*dma_cb_func)(int event);
extern void dma_init(void);
extern void dma_set_callback(int chn, dma_cb_func cb);
#endif /* __DMA_X1000_H__ */

View file

@ -0,0 +1,81 @@
/***************************************************************************
* __________ __ ___.
* 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 "logf.h"
#include "aic-x1000.h"
#include "i2c-x1000.h"
#include "gpio-x1000.h"
#include "x1000/aic.h"
#include "x1000/cpm.h"
void audiohw_init(void)
{
/* Configure AIC for I2S operation */
jz_writef(CPM_CLKGR, AIC(0));
gpio_config(GPIO_B, 0x1f, GPIO_DEVICE(1));
jz_writef(AIC_I2SCR, STPBK(1));
/* Operate as I2S master, use external codec */
jz_writef(AIC_CFG, AUSEL(1), ICDC(0), BCKD(1), SYNCD(1), LSMP(1));
jz_writef(AIC_I2SCR, ESCLK(1), AMSL(0));
/* Stereo audio, packed 16 bit samples */
jz_writef(AIC_CCR, PACK16(1), CHANNEL(1), OSS(1));
/* Initialize DAC */
i2c_x1000_set_freq(AK4376_BUS, I2C_FREQ_400K);
ak4376_init();
}
void audiohw_postinit(void)
{
}
void audiohw_close(void)
{
ak4376_close();
}
void ak4376_set_pdn_pin(int level)
{
gpio_config(GPIO_A, 1 << 16, GPIO_OUTPUT(level ? 1 : 0));
}
int ak4376_set_mclk_freq(int hw_freq, bool enabled)
{
/* Get the multiplier */
int freq = hw_freq_sampr[hw_freq];
int mult = freq >= SAMPR_176 ? 128 : 256;
if(enabled) {
/* Set the new frequency; clock is enabled afterward */
if(aic_i2s_set_mclk(X1000_CLK_SCLK_A, freq, mult))
logf("WARNING: unachievable audio rate %d x %d!?", freq, mult);
} else {
/* Shut off the clock */
jz_writef(AIC_I2SCR, STPBK(1));
}
return mult;
}

View file

@ -0,0 +1,88 @@
/***************************************************************************
* __________ __ ___.
* 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 33000
#define BL_BTN_CHN 4
#define BL_BTN_PERIOD 100000
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_init(BL_BTN_CHN);
pwm_enable(BL_LCD_CHN);
pwm_enable(BL_BTN_CHN);
backlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
buttonlight_hw_brightness(MAX_BRIGHTNESS_SETTING);
/* TODO: avoid buttonlight flicker when powering up the machine */
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);
}
void buttonlight_hw_on(void)
{
pwm_enable(BL_BTN_CHN);
}
void buttonlight_hw_off(void)
{
pwm_disable(BL_BTN_CHN);
}
void buttonlight_hw_brightness(int brightness)
{
/* Duty cycle below 11% seems to turn the buttonlight off entirely,
* so we need to rescale the range */
int duty_ns = backlight_calc_duty(BL_BTN_PERIOD, BL_BTN_PERIOD*11/100, brightness);
pwm_set_period(BL_BTN_CHN, BL_BTN_PERIOD, duty_ns);
}

View file

@ -0,0 +1,37 @@
/***************************************************************************
* __________ __ ___.
* 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 <stdbool.h>
extern bool backlight_hw_init(void);
extern void backlight_hw_on(void);
extern void backlight_hw_off(void);
extern void backlight_hw_brightness(int brightness);
extern void buttonlight_hw_on(void);
extern void buttonlight_hw_off(void);
extern void buttonlight_hw_brightness(int brightness);
#endif /* __BACKLIGHT_TARGET_H__ */

View file

@ -0,0 +1,503 @@
/***************************************************************************
* __________ __ ___.
* 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 "button.h"
#include "kernel.h"
#include "backlight.h"
#include "panic.h"
#include "lcd.h"
#include "gpio-x1000.h"
#include "i2c-x1000.h"
#include <string.h>
#include <stdbool.h>
#ifndef BOOTLOADER
# include "font.h"
#endif
#define FT_RST_PIN (1 << 15)
#define FT_INT_PIN (1 << 12)
#define ft_interrupt GPIOB12
/* Touch event types */
#define EVENT_NONE (-1)
#define EVENT_PRESS 0
#define EVENT_RELEASE 1
#define EVENT_CONTACT 2
/* FSM states */
#define STATE_IDLE 0
#define STATE_PRESS 1
#define STATE_REPORT 2
#define STATE_SCROLL_PRESS 3
#define STATE_SCROLLING 4
/* Assume there's no active touch if no event is reported in this time */
#define AUTORELEASE_TIME (10000 * OST_TICKS_PER_US)
/* If there's no significant motion on the scrollbar for this time,
* then report it as a button press instead */
#define SCROLL_PRESS_TIME (100000 * OST_TICKS_PER_US)
/* If a press on the scrollbar moves more than this during SCROLL_PRESS_TIME,
* then we enter scrolling mode. */
#define MIN_SCROLL_THRESH 15
/* If OST tick a is after OST tick b, then returns the number of ticks
* in the interval between a and b; otherwise undefined. */
#define TICKS_SINCE(a, b) ((a) - (b))
/* Number of touch samples to smooth before reading */
#define TOUCH_SAMPLES 3
static struct ft_driver {
int i2c_cookie;
i2c_descriptor i2c_desc;
uint8_t raw_data[6];
bool active;
/* Number of pixels squared which must be moved before
* a scrollbar pulse is generated */
int scroll_thresh_sqr;
} ftd;
static struct ft_state_machine {
/* Current button state, used by button_read_device() */
int buttons;
/* FSM state */
int state;
/* Time of the last touch event, as 32-bit OST timestamp. The kernel
* tick is simply too low-resolution to work reliably, especially as
* we handle touchpad events asynchronously. */
uint32_t last_event_t;
/* Time of entering the SCROLL_PRESS state, used to differentiate
* between a press, hold, or scrolling motion */
uint32_t scroll_press_t;
/* Number of CONTACT events sampled in the PRESS state.
* Must reach TOUCH_SAMPLES before we move forward. */
int samples;
/* Filter for smoothing touch points */
int sum_x, sum_y;
/* Position of the original touch */
int orig_x, orig_y;
/* Current touch position */
int cur_x, cur_y;
} fsm;
static int touch_to_button(int x, int y)
{
if(x == 900) {
/* Right strip */
if(y == 80)
return BUTTON_BACK;
else if(y == 240)
return BUTTON_RIGHT;
else
return 0;
} else if(x < 80) {
/* Left strip */
if(y < 80)
return BUTTON_MENU;
else if(y > 190)
return BUTTON_LEFT;
else
return 0;
} else {
/* Middle strip */
if(y < 100)
return BUTTON_UP;
else if(y > 220)
return BUTTON_DOWN;
else
return BUTTON_SELECT;
}
}
static bool ft_accum_touch(uint32_t t, int tx, int ty)
{
/* Record event time */
fsm.last_event_t = t;
if(fsm.samples < TOUCH_SAMPLES) {
/* Continue "priming" the filter */
fsm.sum_x += tx;
fsm.sum_y += ty;
fsm.samples += 1;
/* Return if filter is not ready */
if(fsm.samples < TOUCH_SAMPLES)
return false;
} else {
/* Update filter */
fsm.sum_x += tx - fsm.sum_x / TOUCH_SAMPLES;
fsm.sum_y += ty - fsm.sum_y / TOUCH_SAMPLES;
}
/* Filter is ready, so read the point */
fsm.cur_x = fsm.sum_x / TOUCH_SAMPLES;
fsm.cur_y = fsm.sum_y / TOUCH_SAMPLES;
return true;
}
static void ft_go_idle(void)
{
/* Null out the touch state */
fsm.buttons = 0;
fsm.samples = 0;
fsm.sum_x = fsm.sum_y = 0;
fsm.state = STATE_IDLE;
}
static void ft_start_report(void)
{
/* Report the button bit */
fsm.buttons = touch_to_button(fsm.cur_x, fsm.cur_y);
fsm.orig_x = fsm.cur_x;
fsm.orig_y = fsm.cur_y;
fsm.state = STATE_REPORT;
}
static void ft_start_report_or_scroll(void)
{
ft_start_report();
/* If the press occurs on the scrollbar, then we need to
* wait an additional delay before reporting it in case
* this is the beginning of a scrolling motion */
if(fsm.buttons & (BUTTON_UP|BUTTON_DOWN|BUTTON_SELECT)) {
fsm.buttons = 0;
fsm.scroll_press_t = __ost_read32();
fsm.state = STATE_SCROLL_PRESS;
}
}
static void ft_step_state(uint32_t t, int evt, int tx, int ty)
{
/* Generate a release event automatically in case we missed it */
if(evt == EVENT_NONE) {
if(TICKS_SINCE(t, fsm.last_event_t) >= AUTORELEASE_TIME) {
evt = EVENT_RELEASE;
tx = fsm.cur_x;
ty = fsm.cur_y;
}
}
switch(fsm.state) {
case STATE_IDLE: {
if(evt == EVENT_PRESS || evt == EVENT_CONTACT) {
/* Move to REPORT or PRESS state */
if(ft_accum_touch(t, tx, ty))
ft_start_report_or_scroll();
else
fsm.state = STATE_PRESS;
}
} break;
case STATE_PRESS: {
if(evt == EVENT_RELEASE) {
/* Ignore if the number of samples is too low */
ft_go_idle();
} else if(evt == EVENT_PRESS || evt == EVENT_CONTACT) {
/* Accumulate the touch position in the filter */
if(ft_accum_touch(t, tx, ty))
ft_start_report_or_scroll();
}
} break;
case STATE_REPORT: {
if(evt == EVENT_RELEASE)
ft_go_idle();
else if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
ft_accum_touch(t, tx, ty);
} break;
case STATE_SCROLL_PRESS: {
if(evt == EVENT_RELEASE) {
/* This _should_ synthesize a button press.
*
* - ft_start_report() will set the button bit based on the
* current touch position and enter the REPORT state, which
* will automatically hold the bit high
*
* - The next button_read_device() will see the button bit
* and report it back to Rockbox, then step the FSM with
* EVENT_NONE.
*
* - The EVENT_NONE stepping will eventually autogenerate a
* RELEASE event and restore the button state back to 0
*
* - There's a small logic hole in the REPORT state which
* could cause it to miss an immediately repeated PRESS
* that occurs before the autorelease timeout kicks in.
* FIXME: We might want to special-case that.
*/
ft_start_report();
break;
}
if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
ft_accum_touch(t, tx, ty);
int dx = fsm.cur_x - fsm.orig_x;
int dy = fsm.cur_y - fsm.orig_y;
int dp = (dx*dx) + (dy*dy);
if(dp >= MIN_SCROLL_THRESH*MIN_SCROLL_THRESH) {
/* Significant motion: enter SCROLLING state */
fsm.state = STATE_SCROLLING;
} else if(TICKS_SINCE(t, fsm.scroll_press_t) >= SCROLL_PRESS_TIME) {
/* No significant motion: report it as a press */
fsm.cur_x = fsm.orig_x;
fsm.cur_y = fsm.orig_y;
ft_start_report();
}
} break;
case STATE_SCROLLING: {
if(evt == EVENT_RELEASE) {
ft_go_idle();
break;
}
if(evt == EVENT_PRESS || evt == EVENT_CONTACT)
ft_accum_touch(t, tx, ty);
int dx = fsm.cur_x - fsm.orig_x;
int dy = fsm.cur_y - fsm.orig_y;
int dp = (dx*dx) + (dy*dy);
if(dp >= ftd.scroll_thresh_sqr) {
if(dy < 0) {
queue_post(&button_queue, BUTTON_SCROLL_BACK, 0);
} else {
queue_post(&button_queue, BUTTON_SCROLL_FWD, 0);
}
/* Poke the backlight */
backlight_on();
buttonlight_on();
fsm.orig_x = fsm.cur_x;
fsm.orig_y = fsm.cur_y;
}
} break;
default:
panicf("ft6x06: unhandled state");
break;
}
}
static void ft_i2c_callback(int status, i2c_descriptor* desc)
{
(void)desc;
if(status != I2C_STATUS_OK)
return;
/* The panel is oriented such that its X axis is vertical,
* so swap the axes for reporting */
int evt = ftd.raw_data[1] >> 6;
int ty = ftd.raw_data[2] | ((ftd.raw_data[1] & 0xf) << 8);
int tx = ftd.raw_data[4] | ((ftd.raw_data[3] & 0xf) << 8);
/* TODO: convert the touch positions to linear positions.
*
* Points reported by the touch controller are distorted and non-linear,
* ideally we'd like to correct these values. There's more precision in
* the middle of the touchpad than on the edges, so scrolling feels slow
* in the middle and faster near the edge.
*/
ft_step_state(__ost_read32(), evt, tx, ty);
}
void ft_interrupt(void)
{
/* We don't care if this fails */
i2c_async_queue(FT6x06_BUS, TIMEOUT_NOBLOCK, I2C_Q_ONCE,
ftd.i2c_cookie, &ftd.i2c_desc);
}
static void ft_init(void)
{
/* Initialize the driver state */
ftd.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1);
ftd.i2c_desc.slave_addr = FT6x06_ADDR;
ftd.i2c_desc.bus_cond = I2C_START | I2C_STOP;
ftd.i2c_desc.tran_mode = I2C_READ;
ftd.i2c_desc.buffer[0] = &ftd.raw_data[5];
ftd.i2c_desc.count[0] = 1;
ftd.i2c_desc.buffer[1] = &ftd.raw_data[0];
ftd.i2c_desc.count[1] = 5;
ftd.i2c_desc.callback = ft_i2c_callback;
ftd.i2c_desc.arg = 0;
ftd.i2c_desc.next = NULL;
ftd.raw_data[5] = 0x02;
ftd.active = true;
touchpad_set_sensitivity(DEFAULT_TOUCHPAD_SENSITIVITY_SETTING);
/* Initialize the state machine */
fsm.buttons = 0;
fsm.state = STATE_IDLE;
fsm.last_event_t = 0;
fsm.scroll_press_t = 0;
fsm.samples = 0;
fsm.sum_x = fsm.sum_y = 0;
fsm.orig_x = fsm.orig_y = 0;
fsm.cur_x = fsm.cur_y = 0;
/* Bring up I2C bus */
i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K);
/* Reset chip */
gpio_config(GPIO_B, FT_RST_PIN|FT_INT_PIN, GPIO_OUTPUT(0));
mdelay(5);
gpio_out_level(GPIO_B, FT_RST_PIN, 1);
gpio_config(GPIO_B, FT_INT_PIN, GPIO_IRQ_EDGE(0));
gpio_enable_irq(GPIO_B, FT_INT_PIN);
}
void touchpad_set_sensitivity(int level)
{
int pixels = 40;
pixels -= level;
ftd.scroll_thresh_sqr = pixels * pixels;
}
void touchpad_enable_device(bool en)
{
i2c_reg_write1(FT6x06_BUS, FT6x06_ADDR, 0xa5, en ? 0 : 3);
ftd.active = en;
}
/* 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(AXP173_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d);
return HPD_POLL_TIME;
}
static void hp_detect_init(void)
{
static struct timeout tmo;
static const uint8_t gpio_reg = 0x94;
static i2c_descriptor desc = {
.slave_addr = AXP173_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 an undocumented GPIO on the AXP173.
* This sets it to input mode so we can see the pin state. */
i2c_reg_write1(AXP173_BUS, AXP173_ADDR, 0x93, 0x01);
/* Get an initial reading before startup */
int r = i2c_reg_read1(AXP173_BUS, AXP173_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);
}
/* Rockbox interface */
void button_init_device(void)
{
/* Configure physical button GPIOs */
gpio_config(GPIO_A, (1 << 17) | (1 << 19), GPIO_INPUT);
gpio_config(GPIO_B, (1 << 28) | (1 << 31), GPIO_INPUT);
/* Initialize touchpad */
ft_init();
/* Set up headphone detect polling */
hp_detect_init();
}
int button_read_device(void)
{
int r = fsm.buttons;
ft_step_state(__ost_read32(), EVENT_NONE, 0, 0);
/* Read GPIOs for physical buttons */
uint32_t a = REG_GPIO_PIN(GPIO_A);
uint32_t b = REG_GPIO_PIN(GPIO_B);
/* All buttons are active low */
if((a & (1 << 17)) == 0) r |= BUTTON_PLAY;
if((a & (1 << 19)) == 0) r |= BUTTON_VOL_UP;
if((b & (1 << 28)) == 0) r |= BUTTON_VOL_DOWN;
if((b & (1 << 31)) == 0) r |= BUTTON_POWER;
return r;
}
bool headphones_inserted()
{
return hp_detect_reg & 0x40 ? true : false;
}
#ifndef BOOTLOADER
static int getbtn(void)
{
int btn;
do {
btn = button_get_w_tmo(1);
} while(btn & (BUTTON_REL|BUTTON_REPEAT));
return btn;
}
bool dbg_fiiom3k_touchpad(void)
{
static const char* fsm_statenames[] = {
"IDLE", "PRESS", "REPORT", "SCROLL_PRESS", "SCROLLING"
};
do {
int line = 0;
lcd_clear_display();
lcd_putsf(0, line++, "state: %s", fsm_statenames[fsm.state]);
lcd_putsf(0, line++, "button: %08x", fsm.buttons);
lcd_putsf(0, line++, "pos x: %4d orig x: %4d", fsm.cur_x, fsm.orig_x);
lcd_putsf(0, line++, "pos y: %4d orig y: %4d", fsm.cur_y, fsm.orig_y);
lcd_update();
} while(getbtn() != BUTTON_POWER);
return false;
}
#endif

View file

@ -0,0 +1,54 @@
/***************************************************************************
* __________ __ ___.
* 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 <stdbool.h>
#define BUTTON_POWER 0x00000001
#define BUTTON_PLAY 0x00000002
#define BUTTON_VOL_UP 0x00000004
#define BUTTON_VOL_DOWN 0x00000008
#define BUTTON_UP 0x00000010
#define BUTTON_DOWN 0x00000020
#define BUTTON_LEFT 0x00000040
#define BUTTON_RIGHT 0x00000080
#define BUTTON_SELECT 0x00000100
#define BUTTON_BACK 0x00000200
#define BUTTON_MENU 0x00000400
#define BUTTON_SCROLL_FWD 0x00000800
#define BUTTON_SCROLL_BACK 0x00001000
#define BUTTON_MAIN (BUTTON_POWER|BUTTON_VOL_UP|BUTTON_VOL_DOWN|\
BUTTON_PLAY|BUTTON_TOUCHPAD)
#define BUTTON_TOUCHPAD (BUTTON_UP|BUTTON_DOWN|BUTTON_LEFT|BUTTON_RIGHT|\
BUTTON_SELECT|BUTTON_BACK|BUTTON_MENU|\
BUTTON_SCROLL_FWD|BUTTON_SCROLL_BACK)
#define POWEROFF_BUTTON BUTTON_POWER
#define POWEROFF_COUNT 30
extern void touchpad_set_sensitivity(int level);
extern void touchpad_enable_device(bool en);
#endif /* __BUTTON_TARGET_H__ */

View file

@ -0,0 +1,37 @@
/***************************************************************************
* __________ __ ___.
* 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 AK4376_BUS 0
#define AK4376_ADDR 0x10
#define FT6x06_BUS 1
#define FT6x06_ADDR 0x38
#define AXP173_BUS 2
#define AXP173_ADDR 0x34
#endif /* __I2C_TARGET_H__ */

View file

@ -0,0 +1,195 @@
/***************************************************************************
* __________ __ ___.
* 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 "installer.h"
#include "nand-x1000.h"
#include "core_alloc.h"
#include "file.h"
#define INSTALL_SUCCESS 0
#define ERR_FLASH_OPEN_FAILED (-1)
#define ERR_FLASH_ENABLE_WP_FAILED (-2)
#define ERR_FLASH_DISABLE_WP_FAILED (-3)
#define ERR_FLASH_ERASE_FAILED (-4)
#define ERR_FLASH_WRITE_FAILED (-5)
#define ERR_FLASH_READ_FAILED (-6)
#define ERR_OUT_OF_MEMORY (-7)
#define ERR_CANNOT_READ_FILE (-8)
#define ERR_CANNOT_WRITE_FILE (-9)
#define ERR_WRONG_SIZE (-10)
#define BOOT_IMAGE_SIZE (128 * 1024)
static int install_from_buffer(const void* buf)
{
if(nand_open())
return ERR_FLASH_OPEN_FAILED;
int status = INSTALL_SUCCESS;
if(nand_enable_writes(true)) {
status = ERR_FLASH_DISABLE_WP_FAILED;
goto _exit;
}
if(nand_erase_block(0)) {
status = ERR_FLASH_ERASE_FAILED;
goto _exit;
}
if(nand_write_bytes(0, BOOT_IMAGE_SIZE, buf)) {
status = ERR_FLASH_WRITE_FAILED;
goto _exit;
}
if(nand_enable_writes(false)) {
status = ERR_FLASH_ENABLE_WP_FAILED;
goto _exit;
}
_exit:
nand_close();
return status;
}
static int dump_to_buffer(void* buf)
{
if(nand_open())
return ERR_FLASH_OPEN_FAILED;
int status = INSTALL_SUCCESS;
if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) {
status = ERR_FLASH_READ_FAILED;
goto _exit;
}
_exit:
nand_close();
return status;
}
int install_bootloader(const char* path)
{
/* Allocate memory to hold image */
int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE);
if(handle < 0)
return ERR_OUT_OF_MEMORY;
int status = INSTALL_SUCCESS;
void* buffer = core_get_data(handle);
/* Open the boot image */
int fd = open(path, O_RDONLY);
if(fd < 0) {
status = ERR_CANNOT_READ_FILE;
goto _exit;
}
/* Check file size */
off_t fsize = filesize(fd);
if(fsize != BOOT_IMAGE_SIZE) {
status = ERR_WRONG_SIZE;
goto _exit;
}
/* Read the file into the buffer */
ssize_t cnt = read(fd, buffer, BOOT_IMAGE_SIZE);
if(cnt != BOOT_IMAGE_SIZE) {
status = ERR_CANNOT_READ_FILE;
goto _exit;
}
/* Perform the installation */
status = install_from_buffer(buffer);
_exit:
if(fd >= 0)
close(fd);
core_free(handle);
return status;
}
/* Dump the current bootloader to a file */
int dump_bootloader(const char* path)
{
/* Allocate memory to hold image */
int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE);
if(handle < 0)
return -1;
/* Read data from flash */
int fd = -1;
void* buffer = core_get_data(handle);
int status = dump_to_buffer(buffer);
if(status)
goto _exit;
/* Open file */
fd = open(path, O_CREAT|O_TRUNC|O_WRONLY);
if(fd < 0) {
status = ERR_CANNOT_WRITE_FILE;
goto _exit;
}
/* Write data to file */
ssize_t cnt = write(fd, buffer, BOOT_IMAGE_SIZE);
if(cnt != BOOT_IMAGE_SIZE) {
status = ERR_CANNOT_WRITE_FILE;
goto _exit;
}
_exit:
if(fd >= 0)
close(fd);
core_free(handle);
return status;
}
const char* installer_strerror(int rc)
{
switch(rc) {
case INSTALL_SUCCESS:
return "Success";
case ERR_FLASH_OPEN_FAILED:
return "Can't open flash device";
case ERR_FLASH_ENABLE_WP_FAILED:
return "Couldn't re-enable write protect";
case ERR_FLASH_DISABLE_WP_FAILED:
return "Can't disable write protect";
case ERR_FLASH_ERASE_FAILED:
return "Flash erase failed";
case ERR_FLASH_WRITE_FAILED:
return "Flash write error";
case ERR_FLASH_READ_FAILED:
return "Flash read error";
case ERR_OUT_OF_MEMORY:
return "Out of memory";
case ERR_CANNOT_READ_FILE:
return "Error reading file";
case ERR_CANNOT_WRITE_FILE:
return "Error writing file";
case ERR_WRONG_SIZE:
return "Wrong file size";
default:
return "Unknown error";
}
}

View file

@ -0,0 +1,192 @@
/***************************************************************************
* __________ __ ___.
* 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 "kernel.h"
#include "lcd-x1000.h"
#include "gpio-x1000.h"
#include "system.h"
#define CS_PIN (1 << 18)
#define RD_PIN (1 << 16)
static const uint32_t fiio_lcd_cmd_enable[] = {
/* Software reset */
LCD_INSTR_CMD, 0x01,
LCD_INSTR_UDELAY, 120000,
/* Sleep out */
LCD_INSTR_CMD, 0x11,
LCD_INSTR_UDELAY, 5000,
/* Memory access order */
LCD_INSTR_CMD, 0x36,
LCD_INSTR_DAT, 0x00,
/* Row and column address set */
LCD_INSTR_CMD, 0x2a,
LCD_INSTR_DAT, 0x00,
LCD_INSTR_DAT, 0x00,
LCD_INSTR_DAT, (LCD_WIDTH >> 8) & 0xff,
LCD_INSTR_DAT, (LCD_WIDTH & 0xff),
LCD_INSTR_CMD, 0x2b,
LCD_INSTR_DAT, 0x00,
LCD_INSTR_DAT, 0x00,
LCD_INSTR_DAT, (LCD_HEIGHT >> 8) & 0xff,
LCD_INSTR_DAT, (LCD_HEIGHT & 0xff),
/* Interface pixel format */
LCD_INSTR_CMD, 0x3a,
LCD_INSTR_DAT, 0x05,
/* Enable display inversion */
LCD_INSTR_CMD, 0x21,
/* Porch setting */
LCD_INSTR_CMD, 0xb2,
LCD_INSTR_DAT, 0x0c,
LCD_INSTR_DAT, 0x0c,
LCD_INSTR_DAT, 0x00,
LCD_INSTR_DAT, 0x33,
LCD_INSTR_DAT, 0x33,
/* Gate control */
LCD_INSTR_CMD, 0xb7,
LCD_INSTR_DAT, 0x35,
/* VCOM setting */
LCD_INSTR_CMD, 0xbb,
LCD_INSTR_DAT, 0x1f,
/* Backlight control 5 */
LCD_INSTR_CMD, 0xbc,
LCD_INSTR_DAT, 0xec,
/* Backlight control 6 */
LCD_INSTR_CMD, 0xbd,
LCD_INSTR_DAT, 0xfe,
/* Voltage settings */
LCD_INSTR_CMD, 0xc2,
LCD_INSTR_DAT, 0x01,
LCD_INSTR_CMD, 0xc3,
LCD_INSTR_DAT, 0x19,
LCD_INSTR_CMD, 0xc4,
LCD_INSTR_DAT, 0x20,
/* Frame rate control */
LCD_INSTR_CMD, 0xc6,
LCD_INSTR_DAT, 0x0f, /* = 60 fps */
/* Power control 1 */
LCD_INSTR_CMD, 0xd0,
LCD_INSTR_DAT, 0xa4,
LCD_INSTR_DAT, 0xa1,
/* d6 Unknown */
LCD_INSTR_CMD, 0xd6,
LCD_INSTR_DAT, 0xa1,
/* Positive gamma correction */
LCD_INSTR_CMD, 0xe0,
LCD_INSTR_DAT, 0xd0,
LCD_INSTR_DAT, 0x06,
LCD_INSTR_DAT, 0x0c,
LCD_INSTR_DAT, 0x0a,
LCD_INSTR_DAT, 0x09,
LCD_INSTR_DAT, 0x0a,
LCD_INSTR_DAT, 0x32,
LCD_INSTR_DAT, 0x33,
LCD_INSTR_DAT, 0x49,
LCD_INSTR_DAT, 0x19,
LCD_INSTR_DAT, 0x14,
LCD_INSTR_DAT, 0x15,
LCD_INSTR_DAT, 0x2b,
LCD_INSTR_DAT, 0x34,
/* Negative gamma correction */
LCD_INSTR_CMD, 0xe1,
LCD_INSTR_DAT, 0xd0,
LCD_INSTR_DAT, 0x06,
LCD_INSTR_DAT, 0x0c,
LCD_INSTR_DAT, 0x0a,
LCD_INSTR_DAT, 0x09,
LCD_INSTR_DAT, 0x11,
LCD_INSTR_DAT, 0x37,
LCD_INSTR_DAT, 0x33,
LCD_INSTR_DAT, 0x49,
LCD_INSTR_DAT, 0x19,
LCD_INSTR_DAT, 0x14,
LCD_INSTR_DAT, 0x15,
LCD_INSTR_DAT, 0x2d,
LCD_INSTR_DAT, 0x34,
/* Tearing effect line ON, mode=0 (vsync signal) */
LCD_INSTR_CMD, 0x35,
LCD_INSTR_DAT, 0x00,
/* Display ON */
LCD_INSTR_CMD, 0x29,
LCD_INSTR_END,
};
static const uint32_t fiio_lcd_cmd_sleep[] = {
/* Display OFF */
LCD_INSTR_CMD, 0x28,
/* Sleep IN */
LCD_INSTR_CMD, 0x10,
LCD_INSTR_UDELAY, 5000,
LCD_INSTR_END,
};
static const uint32_t fiio_lcd_cmd_wake[] = {
/* Sleep OUT */
LCD_INSTR_CMD, 0x11,
LCD_INSTR_UDELAY, 5000,
/* Display ON */
LCD_INSTR_CMD, 0x29,
LCD_INSTR_END,
};
static const uint8_t __attribute__((aligned(64)))
fiio_lcd_dma_wr_cmd[] = {0x00, 0x00, 0x00, 0x2c};
const struct lcd_tgt_config lcd_tgt_config = {
.bus_width = 16,
.cmd_width = 8,
.use_6800_mode = 0,
.use_serial = 0,
.clk_polarity = 0,
.dc_polarity = 0,
.wr_polarity = 1,
.te_enable = 1,
.te_polarity = 1,
.te_narrow = 0,
.dma_wr_cmd_buf = &fiio_lcd_dma_wr_cmd,
.dma_wr_cmd_size = sizeof(fiio_lcd_dma_wr_cmd),
};
void lcd_tgt_enable(bool enable)
{
if(enable) {
gpio_config(GPIO_A, 0xffff, GPIO_DEVICE(1));
gpio_config(GPIO_B, 0x1f << 16, GPIO_DEVICE(1));
gpio_config(GPIO_B, CS_PIN|RD_PIN, GPIO_OUTPUT(1));
mdelay(5);
gpio_out_level(GPIO_B, CS_PIN, 0);
lcd_set_clock(X1000_CLK_SCLK_A, 30000000);
lcd_exec_commands(&fiio_lcd_cmd_enable[0]);
} else {
lcd_exec_commands(&fiio_lcd_cmd_sleep[0]);
mdelay(115); /* ensure we wait a total of 120ms before power off */
gpio_config(GPIO_B, CS_PIN|RD_PIN, 0);
}
}
void lcd_tgt_sleep(bool sleep)
{
if(sleep)
lcd_exec_commands(&fiio_lcd_cmd_sleep[0]);
else
lcd_exec_commands(&fiio_lcd_cmd_wake[0]);
}

View file

@ -0,0 +1,53 @@
/***************************************************************************
* __________ __ ___.
* 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 "nand-x1000.h"
#include "nand-target.h"
#include "sfc-x1000.h"
/* Unbelievably FiiO has completely disabled the use of ECC for this chip
* in their Linux kernel, even though it has perfectly good spare areas.
* There's no internal ECC either.
*
* Using nanddump to read the spare areas reveals they're filled with 0xff,
* and the publicly released Linux source has the ecc_strength set to 0.
*/
static const nand_chip_data ato25d1ga = {
.name = "ATO25D1GA",
.mf_id = 0x9b,
.dev_id = 0x12,
.dev_conf = NAND_INIT_SFC_DEV_CONF,
/* XXX: datasheet says 104 MHz but FiiO seems to run this at 150 MHz.
* Didn't find any issues doing this so might as well keep the behavior.
*/
.clock_freq = NAND_INIT_CLOCK_SPEED,
.block_size = 64,
.page_size = 2048,
.spare_size = 64,
.rowaddr_width = 3,
.coladdr_width = 2,
.flags = NANDCHIP_FLAG_QUAD,
};
const nand_chip_desc target_nand_chip_descs[] = {
{&ato25d1ga, &nand_chip_ops_std},
{NULL, NULL},
};

View file

@ -0,0 +1,42 @@
/***************************************************************************
* __________ __ ___.
* 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 __NAND_TARGET_H__
#define __NAND_TARGET_H__
/* The max page size (main + spare) of all NAND chips used by this target */
#define NAND_MAX_PAGE_SIZE (2048 + 64)
/* The clock source to use for the SFC controller. Note the SPL has special
* handling which ignores this choice, so it only applies to bootloader & app.
*/
#define NAND_CLOCK_SOURCE X1000_CLK_SCLK_A
/* The clock speed to use for the SFC controller during chip identification */
#define NAND_INIT_CLOCK_SPEED 150000000
/* Initial value to program SFC_DEV_CONF register with */
#define NAND_INIT_SFC_DEV_CONF \
jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), \
CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), \
STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1))
#endif /* __NAND_TARGET_H__ */

View file

@ -0,0 +1,97 @@
/***************************************************************************
* __________ __ ___.
* 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 "kernel.h"
#include "axp173.h"
#include "i2c-x1000.h"
#include "gpio-x1000.h"
const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] =
{
3470
};
/* the OF shuts down at this voltage */
const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] =
{
3400
};
/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */
const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] =
{
{ 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 }
};
/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */
const unsigned short const percent_to_volt_charge[11] =
{
3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196
};
#define AXP173_IRQ_PORT GPIO_B
#define AXP173_IRQ_PIN (1 << 10)
void power_init(void)
{
/* Initialize driver */
i2c_x1000_set_freq(2, I2C_FREQ_400K);
axp173_init();
/* Set lowest sample rate */
axp173_adc_set_rate(AXP173_ADC_RATE_25HZ);
/* Ensure battery voltage ADC is enabled */
int bits = axp173_adc_get_enabled();
bits |= (1 << ADC_BATTERY_VOLTAGE);
axp173_adc_set_enabled(bits);
/* Turn on all power outputs */
i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x12, 0, 0x5f, NULL);
i2c_reg_modify1(AXP173_BUS, AXP173_ADDR, 0x80, 0, 0xc0, NULL);
/* Short delay to give power outputs time to stabilize */
mdelay(5);
}
void adc_init(void)
{
}
void power_off(void)
{
/* Set the shutdown bit */
i2c_reg_setbit1(AXP173_BUS, AXP173_ADDR, 0x32, 7, 1, NULL);
while(1);
}
bool charging_state(void)
{
return axp173_battery_status() == AXP173_BATT_CHARGING;
}
int _battery_voltage(void)
{
return axp173_adc_read(ADC_BATTERY_VOLTAGE);
}

View file

@ -0,0 +1,84 @@
/***************************************************************************
* __________ __ ___.
* 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 "gpio-x1000.h"
#include "kernel.h"
#ifndef BOOTLOADER_SPL
struct mutex gpio_z_mutex;
#endif
void gpio_init(void)
{
#ifndef BOOTLOADER_SPL
mutex_init(&gpio_z_mutex);
#endif
/* Set all pins to input state */
for(int i = 0; i < 4; ++i) {
jz_clr(GPIO_INT(GPIO_Z), 0xffffffff);
jz_set(GPIO_MSK(GPIO_Z), 0xffffffff);
jz_set(GPIO_PAT1(GPIO_Z), 0xffffffff);
jz_clr(GPIO_PAT0(GPIO_Z), 0xffffffff);
REG_GPIO_Z_GID2LD = i;
}
/* Clear flag and disable pull resistor */
for(int i = 0; i < 4; ++i) {
jz_clr(GPIO_FLAG(i), 0xffffffff);
jz_set(GPIO_PULL(i), 0xffffffff);
}
}
void gpio_lock(void)
{
#ifndef BOOTLOADER_SPL
mutex_lock(&gpio_z_mutex);
#endif
}
void gpio_unlock(void)
{
#ifndef BOOTLOADER_SPL
mutex_unlock(&gpio_z_mutex);
#endif
}
void gpio_config(int port, unsigned pinmask, int func)
{
unsigned intr = REG_GPIO_INT(port);
unsigned mask = REG_GPIO_MSK(port);
unsigned pat1 = REG_GPIO_PAT1(port);
unsigned pat0 = REG_GPIO_PAT0(port);
gpio_lock();
if(func & 8) jz_set(GPIO_INT(GPIO_Z), (intr & pinmask) ^ pinmask);
else jz_clr(GPIO_INT(GPIO_Z), (~intr & pinmask) ^ pinmask);
if(func & 4) jz_set(GPIO_MSK(GPIO_Z), (mask & pinmask) ^ pinmask);
else jz_clr(GPIO_MSK(GPIO_Z), (~mask & pinmask) ^ pinmask);
if(func & 2) jz_set(GPIO_PAT1(GPIO_Z), (pat1 & pinmask) ^ pinmask);
else jz_clr(GPIO_PAT1(GPIO_Z), (~pat1 & pinmask) ^ pinmask);
if(func & 1) jz_set(GPIO_PAT0(GPIO_Z), (pat0 & pinmask) ^ pinmask);
else jz_clr(GPIO_PAT0(GPIO_Z), (~pat0 & pinmask) ^ pinmask);
REG_GPIO_Z_GID2LD = port;
gpio_unlock();
gpio_set_pull(port, pinmask, func & 16);
}

View file

@ -0,0 +1,110 @@
/***************************************************************************
* __________ __ ___.
* 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 __GPIO_X1000_H__
#define __GPIO_X1000_H__
/* GPIO API
* --------
*
* To assign a new function to a GPIO, call gpio_config(). This uses the
* hardware's GPIO Z facility to atomically most GPIO registers at once,
* so it can be used to make any state transition safely. Since GPIO Z is
* protected by a mutex, you can't call gpio_config() from interrupt context.
*
* If you need to use GPIO Z directly, then use gpio_lock() and gpio_unlock()
* to acquire the mutex.
*
* Depending on the current GPIO state, certain state transitions are safe to
* perform without locking, as they only change one register:
*
* - for pins in GPIO_OUTPUT state:
* - use gpio_out_level() to change the output level
*
* - for pins in GPIO_IRQ_LEVEL or GPIO_IRQ_EDGE state:
* - use gpio_irq_level() to change the trigger level
* - use gpio_irq_mask() to mask/unmask the IRQ
*
* - for pins in GPIO_DEVICE or GPIO_INPUT state:
* - no special transitions allowed
*
* - in all states:
* - use gpio_set_pull() to change the pull-up/pull-down state
*/
#include "x1000/gpio.h"
/* GPIO port numbers */
#define GPIO_A 0
#define GPIO_B 1
#define GPIO_C 2
#define GPIO_D 3
#define GPIO_Z 7
/* GPIO function bits */
#define GPIO_F_PULL 16
#define GPIO_F_INT 8
#define GPIO_F_MASK 4
#define GPIO_F_PAT1 2
#define GPIO_F_PAT0 1
/* GPIO function numbers */
#define GPIO_DEVICE(i) ((i)&3)
#define GPIO_OUTPUT(i) (0x4|((i)&1))
#define GPIO_INPUT 0x16
#define GPIO_IRQ_LEVEL(i) (0x1c|((i)&1))
#define GPIO_IRQ_EDGE(i) (0x1e|((i)&1))
extern void gpio_init(void);
extern void gpio_lock(void);
extern void gpio_unlock(void);
extern void gpio_config(int port, unsigned pinmask, int func);
static inline void gpio_out_level(int port, unsigned pinmask, int level)
{
if(level)
jz_set(GPIO_PAT0(port), pinmask);
else
jz_clr(GPIO_PAT0(port), pinmask);
}
#define gpio_irq_level gpio_out_level
static inline void gpio_irq_mask(int port, unsigned pinmask, int masked)
{
if(masked)
jz_set(GPIO_MSK(port), pinmask);
else
jz_clr(GPIO_MSK(port), pinmask);
}
#define gpio_enable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 0)
#define gpio_disable_irq(port, pinmask) gpio_irq_mask((port), (pinmask), 1)
static inline void gpio_set_pull(int port, unsigned pinmask, int state)
{
if(state)
jz_set(GPIO_PULL(port), pinmask);
else
jz_clr(GPIO_PULL(port), pinmask);
}
#endif /* __GPIO_X1000_H__ */

View file

@ -0,0 +1,478 @@
/***************************************************************************
* __________ __ ___.
* 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.
*
****************************************************************************/
/* #define LOGF_ENABLE */
#include "i2c-x1000.h"
#include "system.h"
#include "kernel.h"
#include "panic.h"
#include "logf.h"
#include "gpio-x1000.h"
#include "clk-x1000.h"
#include "irq-x1000.h"
#include "x1000/i2c.h"
#include "x1000/cpm.h"
#if I2C_ASYNC_BUS_COUNT != 3
# error "Wrong I2C_ASYNC_BUS_COUNT (should be 3)"
#endif
#define FIFO_SIZE 64 /* Size of data FIFOs */
#define FIFO_TX_THRESH 16 /* Wake up when TX FIFO gets to this level */
#define FIFO_RX_SLACK 2 /* Slack space to leave, avoids RX FIFO overflow */
typedef struct i2c_x1000_bus {
/* Hardware channel, this is just equal to i2c-async bus number. */
int chn;
/* Buffer/count usage depends on what phase the bus is processing:
*
* - Phase1: writing out descriptor's buffer[0] for both READs and WRITEs.
* - Phase2: writing out descriptor's buffer[1] for WRITEs, or issuing a
* series of read requests for READs.
*
* In phase1, buffer[1] and count[1] are equal to the descriptor's copy.
* buffer[0] and count[0] get incremented/decremented as we send bytes.
* Phase1 is only visited if we actually need to send bytes; if there
* would be no data in this phase then __i2c_async_submit() sets up the
* driver to go directly to phase2.
*
* Phase2 begins after phase1 writes out its last byte, or if phase1 was
* skipped at submit time. For WRITEs phase2 is identical to phase1 so we
* copy over buffer[1] and count[1] to buffer[0] and count[0], and zero
* out buffer[1] and count[1].
*
* For READs phase2 sets buffer[0] to NULL and count[0] equal to count[1].
* Now count[0] counts the number of bytes left to request, and count[1]
* counts the number of bytes left to receive. i2c_x1000_fifo_write() sees
* that buffer[0] is NULL and sends read requests instead of data bytes.
* buffer[1] is advanced by i2c_x1000_fifo_read() we receive bytes.
*/
unsigned char* buffer[2];
int count[2];
bool phase1;
/* Copied fields from descriptor */
uint8_t bus_cond;
uint8_t tran_mode;
/* Counter to keep track of when to send end conditions */
int byte_cnt;
int byte_cnt_end;
/* Current bus frequency, used to calculate timeout durations */
long freq;
/* Timeout to reset the bus in case of buggy devices */
struct timeout tmo;
/* Flag used to indicate a reset is processing */
int resetting;
} i2c_x1000_bus;
static i2c_x1000_bus i2c_x1000_busses[3];
static void i2c_x1000_fifo_write(i2c_x1000_bus* bus)
{
int tx_free, tx_n;
/* Get free space in FIFO */
tx_free = FIFO_SIZE - REG_I2C_TXFLR(bus->chn);
_again:
/* Leave some slack space when reading. If we submit a full FIFO's worth
* of read requests, there's a small chance that a byte "on the wire" is
* unaccounted for and causes an RX FIFO overrun. Slack space is meant to
* avoid this situation.
*/
if(bus->tran_mode == I2C_READ) {
tx_free -= FIFO_RX_SLACK;
if(tx_free <= 0)
goto _end;
}
/* Calculate number of bytes needed to send/request */
tx_n = MIN(tx_free, bus->count[0]);
/* Account for bytes we're about to send/request */
bus->count[0] -= tx_n;
tx_free -= tx_n;
for(; tx_n > 0; --tx_n) {
bus->byte_cnt += 1;
/* Read data byte or set read request bit */
uint32_t dc = bus->buffer[0] ? *bus->buffer[0]++ : jz_orm(I2C_DC, CMD);
/* Check for first byte & apply RESTART.
* Note the HW handles RESTART automatically when changing the
* direction of the transfer, so we don't need to check for that.
*/
if(bus->byte_cnt == 1 && (bus->bus_cond & I2C_RESTART))
dc |= jz_orm(I2C_DC, RESTART);
/* Check for last byte & apply STOP */
if(bus->byte_cnt == bus->byte_cnt_end && (bus->bus_cond & I2C_STOP))
dc |= jz_orm(I2C_DC, STOP);
/* Add entry to FIFO */
REG_I2C_DC(bus->chn) = dc;
}
/* FIFO full and current phase still has data to send.
* Configure interrupt to fire when there's a good amount of free space.
*/
if(bus->count[0] > 0) {
_end:
REG_I2C_TXTL(bus->chn) = FIFO_TX_THRESH;
jz_writef(I2C_INTMSK(bus->chn), TXEMP(1), RXFL(0));
return;
}
/* Advance to second phase if needed */
if(bus->phase1 && bus->count[1] > 0) {
bus->buffer[0] = bus->tran_mode == I2C_WRITE ? bus->buffer[1] : NULL;
bus->count[0] = bus->count[1];
bus->phase1 = false;
/* Submit further data if possible; else wait for TX space */
if(tx_free > 0)
goto _again;
else
goto _end;
}
/* All phases are done. Now we just need to wake up when the whole
* operation is complete, either by waiting for TX to drain or RX to
* fill to the appropriate level. */
if(bus->tran_mode == I2C_WRITE) {
REG_I2C_TXTL(bus->chn) = 0;
jz_writef(I2C_INTMSK(bus->chn), TXEMP(1), RXFL(0));
} else {
REG_I2C_RXTL(bus->chn) = bus->count[1] - 1;
jz_writef(I2C_INTMSK(bus->chn), TXEMP(0), RXFL(1));
}
}
static void i2c_x1000_fifo_read(i2c_x1000_bus* bus)
{
/* Get number of bytes in the RX FIFO */
int rx_n = REG_I2C_RXFLR(bus->chn);
/* Shouldn't happen, but check just in case */
if(rx_n > bus->count[1]) {
panicf("i2c_x1000(%d): expected %d bytes in RX fifo, got %d",
bus->chn, bus->count[1], rx_n);
}
/* Fill buffer with data from FIFO */
bus->count[1] -= rx_n;
for(; rx_n != 0; --rx_n) {
*bus->buffer[1]++ = jz_readf(I2C_DC(bus->chn), DAT);
}
}
static void i2c_x1000_interrupt(i2c_x1000_bus* bus)
{
int intr = REG_I2C_INTST(bus->chn);
int status = I2C_STATUS_OK;
/* Bus error; we can't prevent this from happening. As I understand
* it, we cannot get a TXABT when the bus is idle, so it should be
* safe to leave this interrupt unmasked all the time.
*/
if(intr & jz_orm(I2C_INTST, TXABT)) {
logf("i2c_x1000(%d): got TXABT (%08lx)",
bus->chn, REG_I2C_ABTSRC(bus->chn));
REG_I2C_CTXABT(bus->chn);
status = I2C_STATUS_ERROR;
goto _done;
}
/* FIFO errors shouldn't occur unless driver did something dumb */
if(intr & jz_orm(I2C_INTST, RXUF, TXOF, RXOF)) {
#if 1
panicf("i2c_x1000(%d): fifo error (%08x)", bus->chn, intr);
#else
/* This is how the error condition would be cleared */
REG_I2C_CTXOF(bus->chn);
REG_I2C_CRXOF(bus->chn);
REG_I2C_CRXUF(bus->chn);
status = I2C_STATUS_ERROR;
goto _done;
#endif
}
/* Read from FIFO on reads, and check if we have sent/received
* the expected amount of data. If so, complete the descriptor. */
if(bus->tran_mode == I2C_READ) {
i2c_x1000_fifo_read(bus);
if(bus->count[1] == 0)
goto _done;
} else if(bus->count[0] == 0) {
goto _done;
}
/* Still need to send or request data -- issue commands to FIFO */
i2c_x1000_fifo_write(bus);
return;
_done:
jz_writef(I2C_INTMSK(bus->chn), TXEMP(0), RXFL(0));
timeout_cancel(&bus->tmo);
__i2c_async_complete_callback(bus->chn, status);
}
void I2C0(void)
{
i2c_x1000_interrupt(&i2c_x1000_busses[0]);
}
void I2C1(void)
{
i2c_x1000_interrupt(&i2c_x1000_busses[1]);
}
void I2C2(void)
{
i2c_x1000_interrupt(&i2c_x1000_busses[2]);
}
static int i2c_x1000_bus_timeout(struct timeout* tmo)
{
/* Buggy device is preventing the operation from completing, so we
* can't do much except reset the bus and hope for the best. Device
* drivers can aid us by detecting the TIMEOUT status we return and
* resetting the device to get it out of a bugged state. */
i2c_x1000_bus* bus = (i2c_x1000_bus*)tmo->data;
switch(bus->resetting) {
default:
/* Start of reset. Disable the controller */
REG_I2C_INTMSK(bus->chn) = 0;
bus->resetting = 1;
jz_writef(I2C_ENABLE(bus->chn), ACTIVE(0));
return 1;
case 1:
/* Check if controller is disabled yet */
if(jz_readf(I2C_ENBST(bus->chn), ACTIVE))
return 1;
/* Wait 10 ms after disabling to give time for bus to clear */
bus->resetting = 2;
return HZ/100;
case 2:
/* Re-enable the controller */
bus->resetting = 3;
jz_writef(I2C_ENABLE(bus->chn), ACTIVE(1));
return 1;
case 3:
/* Check that controller is enabled */
if(jz_readf(I2C_ENBST(bus->chn), ACTIVE) == 0)
return 1;
/* Reset complete */
bus->resetting = 0;
jz_overwritef(I2C_INTMSK(bus->chn), RXFL(0), TXEMP(0),
TXABT(1), TXOF(1), RXOF(1), RXUF(1));
__i2c_async_complete_callback(bus->chn, I2C_STATUS_TIMEOUT);
return 0;
}
}
void __i2c_async_submit(int busnr, i2c_descriptor* desc)
{
i2c_x1000_bus* bus = &i2c_x1000_busses[busnr];
if(desc->tran_mode == I2C_READ) {
if(desc->count[0] > 0) {
/* Handle initial write as phase1 */
bus->buffer[0] = desc->buffer[0];
bus->count[0] = desc->count[0];
bus->phase1 = true;
} else {
/* No initial write, skip directly to phase2 */
bus->buffer[0] = NULL;
bus->count[0] = desc->count[1];
bus->phase1 = false;
}
/* Set buffer/count for phase2 read */
bus->buffer[1] = desc->buffer[1];
bus->count[1] = desc->count[1];
} else {
/* Writes always have to have buffer[0] populated; buffer[1] is
* allowed to be NULL (and thus count[1] = 0). This matches our
* phase logic so no need for anything special
*/
bus->buffer[0] = desc->buffer[0];
bus->count[0] = desc->count[0];
bus->buffer[1] = desc->buffer[1];
bus->count[1] = desc->count[1];
bus->phase1 = true;
}
/* Save bus condition and transfer mode */
bus->bus_cond = desc->bus_cond;
bus->tran_mode = desc->tran_mode;
/* Byte counter is used to check for first and last byte and apply
* the requested end mode */
bus->byte_cnt = 0;
bus->byte_cnt_end = desc->count[0] + desc->count[1];
/* Ensure interrupts are cleared */
REG_I2C_CINT(busnr);
/* Program target address */
jz_overwritef(I2C_TAR(busnr), ADDR(desc->slave_addr & ~I2C_10BIT_ADDR),
10BITS(desc->slave_addr & I2C_10BIT_ADDR ? 1 : 0));
/* Do the initial FIFO fill; this sets up the needed interrupts. */
i2c_x1000_fifo_write(bus);
/* Software timeout to deal with buggy slave devices that pull the bus
* high forever and leave us hanging. Use 100ms + whatever time should
* be needed to handle data transmission. Account for 9 bits per byte
* because of the ACKs necessary after each byte.
*/
long ticks = (HZ/10) + (HZ * 9 * bus->byte_cnt_end / bus->freq);
timeout_register(&bus->tmo, i2c_x1000_bus_timeout, ticks, (intptr_t)bus);
}
void i2c_init(void)
{
/* Initialize core */
__i2c_async_init();
/* Initialize our bus data structures */
for(int i = 0; i < 3; ++i) {
i2c_x1000_busses[i].chn = i;
i2c_x1000_busses[i].freq = 0;
i2c_x1000_busses[i].resetting = 0;
}
}
/* Stuff only required during initialization is below, basically the same as
* the old driver except for how the IRQs are initially set up. */
static const struct {
int port;
unsigned pins;
int func;
} i2c_x1000_gpio_data[] = {
{GPIO_B, 3 << 23, GPIO_DEVICE(0)},
{GPIO_C, 3 << 26, GPIO_DEVICE(0)},
/* Note: I2C1 is also on the following pins (normally used by LCD) */
/* {GPIO_A, 3 << 0, GPIO_DEVICE(2)}, */
{GPIO_D, 3 << 0, GPIO_DEVICE(1)},
};
static void i2c_x1000_gate(int chn, int gate)
{
switch(chn) {
case 0: jz_writef(CPM_CLKGR, I2C0(gate)); break;
case 1: jz_writef(CPM_CLKGR, I2C1(gate)); break;
case 2: jz_writef(CPM_CLKGR, I2C2(gate)); break;
default: break;
}
}
static void i2c_x1000_enable(int chn)
{
/* Enable controller */
jz_writef(I2C_ENABLE(chn), ACTIVE(1));
while(jz_readf(I2C_ENBST(chn), ACTIVE) == 0);
/* Set up interrutpts */
jz_overwritef(I2C_INTMSK(chn), RXFL(0), TXEMP(0),
TXABT(1), TXOF(1), RXOF(1), RXUF(1));
system_enable_irq(IRQ_I2C(chn));
}
static void i2c_x1000_disable(int chn)
{
/* Disable interrupts */
system_disable_irq(IRQ_I2C(chn));
REG_I2C_INTMSK(chn) = 0;
/* Disable controller */
jz_writef(I2C_ENABLE(chn), ACTIVE(0));
while(jz_readf(I2C_ENBST(chn), ACTIVE));
}
void i2c_x1000_set_freq(int chn, int freq)
{
/* Store frequency */
i2c_x1000_busses[chn].freq = freq;
/* Disable the channel if previously active */
i2c_x1000_gate(chn, 0);
if(jz_readf(I2C_ENBST(chn), ACTIVE) == 1)
i2c_x1000_disable(chn);
/* Request to shut down the channel */
if(freq == 0) {
i2c_x1000_gate(chn, 1);
return;
}
/* Calculate timing parameters */
unsigned pclk = clk_get(X1000_CLK_PCLK);
unsigned t_SU_DAT = pclk / (freq * 8);
unsigned t_HD_DAT = pclk / (freq * 12);
unsigned t_LOW = pclk / (freq * 2);
unsigned t_HIGH = pclk / (freq * 2);
if(t_SU_DAT > 1) t_SU_DAT -= 1;
if(t_SU_DAT > 255) t_SU_DAT = 255;
if(t_SU_DAT == 0) t_SU_DAT = 0;
if(t_HD_DAT > 0xffff) t_HD_DAT = 0xfff;
if(t_LOW < 8) t_LOW = 8;
if(t_HIGH < 6) t_HIGH = 6;
/* Control register setting */
unsigned reg = jz_orf(I2C_CON, SLVDIS(1), RESTART(1), MD(1));
if(freq <= I2C_FREQ_100K)
reg |= jz_orf(I2C_CON, SPEED_V(100K));
else
reg |= jz_orf(I2C_CON, SPEED_V(400K));
/* Write the new controller settings */
jz_write(I2C_CON(chn), reg);
jz_write(I2C_SDASU(chn), t_SU_DAT);
jz_write(I2C_SDAHD(chn), t_HD_DAT);
if(freq <= I2C_FREQ_100K) {
jz_write(I2C_SLCNT(chn), t_LOW);
jz_write(I2C_SHCNT(chn), t_HIGH);
} else {
jz_write(I2C_FLCNT(chn), t_LOW);
jz_write(I2C_FHCNT(chn), t_HIGH);
}
/* Claim pins */
gpio_config(i2c_x1000_gpio_data[chn].port,
i2c_x1000_gpio_data[chn].pins,
i2c_x1000_gpio_data[chn].func);
/* Enable the controller */
i2c_x1000_enable(chn);
}

View file

@ -0,0 +1,44 @@
/***************************************************************************
* __________ __ ___.
* 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_X1000_H__
#define __I2C_X1000_H__
#include "i2c-async.h"
#define I2C_FREQ_100K 100000
#define I2C_FREQ_400K 400000
extern void i2c_init(void);
/* Configure the I2C controller prior to use.
*
* - freq: frequency of SCL, should be <= 400 KHz and >= 25 KHz
* - use I2C_FREQ_100K for 100 KHz
* - use I2C_FREQ_400K for 400 Khz
* - use 0 to disable the controller completely
* - frequencies below 25 KHz will violate timing constraints
*
* TODO: move this to the i2c-async API, it's simple enough
*/
extern void i2c_x1000_set_freq(int chn, int freq);
#endif /* __I2C_X1000_H__ */

View file

@ -0,0 +1,115 @@
/***************************************************************************
* __________ __ ___.
* 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 __IRQ_X1000_H__
#define __IRQ_X1000_H__
/* INTC(0) interrupts */
#define IRQ0_DMIC 0
#define IRQ0_AIC 1
#define IRQ0_SFC 7
#define IRQ0_SSI 8
#define IRQ0_PDMA 10
#define IRQ0_PDMAD 11
#define IRQ0_GPIO3 14
#define IRQ0_GPIO2 15
#define IRQ0_GPIO1 16
#define IRQ0_GPIO0 17
#define IRQ0_OTG 21
#define IRQ0_AES 23
#define IRQ0_TCU2 25
#define IRQ0_TCU1 26
#define IRQ0_TCU0 27
#define IRQ0_CIM 30
#define IRQ0_LCD 31
/* INTC(1) interrupts */
#define IRQ1_RTC 0
#define IRQ1_MSC1 4
#define IRQ1_MSC0 5
#define IRQ1_SCC 6
#define IRQ1_PCM 8
#define IRQ1_HARB2 12
#define IRQ1_HARB0 14
#define IRQ1_CPM 15
#define IRQ1_UART2 17
#define IRQ1_UART1 18
#define IRQ1_UART0 19
#define IRQ1_DDR 20
#define IRQ1_EFUSE 22
#define IRQ1_MAC 23
#define IRQ1_I2C2 26
#define IRQ1_I2C1 27
#define IRQ1_I2C0 28
#define IRQ1_I2C(c) (28 - (c))
#define IRQ1_PDMAM 29
#define IRQ1_JPEG 30
/* Unified IRQ numbers */
#define IRQ_DMIC IRQ0_DMIC
#define IRQ_AIC IRQ0_AIC
#define IRQ_SFC IRQ0_SFC
#define IRQ_SSI IRQ0_SSI
#define IRQ_PDMA IRQ0_PDMA
#define IRQ_PDMAD IRQ0_PDMAD
#define IRQ_GPIO3 IRQ0_GPIO3
#define IRQ_GPIO2 IRQ0_GPIO2
#define IRQ_GPIO1 IRQ0_GPIO1
#define IRQ_GPIO0 IRQ0_GPIO0
#define IRQ_OTG IRQ0_OTG
#define IRQ_AES IRQ0_AES
#define IRQ_TCU2 IRQ0_TCU2
#define IRQ_TCU1 IRQ0_TCU1
#define IRQ_TCU0 IRQ0_TCU0
#define IRQ_CIM IRQ0_CIM
#define IRQ_LCD IRQ0_LCD
#define IRQ_RTC (32+IRQ1_RTC)
#define IRQ_MSC1 (32+IRQ1_MSC1)
#define IRQ_MSC0 (32+IRQ1_MSC0)
#define IRQ_SCC (32+IRQ1_SCC)
#define IRQ_PCM (32+IRQ1_PCM)
#define IRQ_HARB2 (32+IRQ1_HARB2)
#define IRQ_HARB0 (32+IRQ1_HARB0)
#define IRQ_CPM (32+IRQ1_CPM)
#define IRQ_UART2 (32+IRQ1_UART2)
#define IRQ_UART1 (32+IRQ1_UART1)
#define IRQ_UART0 (32+IRQ1_UART0)
#define IRQ_DDR (32+IRQ1_DDR)
#define IRQ_EFUSE (32+IRQ1_EFUSE)
#define IRQ_MAC (32+IRQ1_MAC)
#define IRQ_I2C2 (32+IRQ1_I2C2)
#define IRQ_I2C1 (32+IRQ1_I2C1)
#define IRQ_I2C0 (32+IRQ1_I2C0)
#define IRQ_I2C(c) (32+IRQ1_I2C(c))
#define IRQ_PDMAM (32+IRQ1_PDMAM)
#define IRQ_JPEG (32+IRQ1_JPEG)
#define IRQ_GPIO(port, pin) (64 + 32*(port) + (pin))
#define IRQ_IS_GROUP0(irq) (((irq) & 0xffffff20) == 0x00)
#define IRQ_IS_GROUP1(irq) (((irq) & 0xffffff20) == 0x20)
#define IRQ_IS_GPIO(irq) ((irq) >= 64)
#define IRQ_TO_GROUP0(irq) (irq)
#define IRQ_TO_GROUP1(irq) ((irq) - 32)
#define IRQ_TO_GPIO_PORT(irq) (((irq) - 64) >> 5)
#define IRQ_TO_GPIO_PIN(irq) (((irq) - 64) & 0x1f)
#endif /* __IRQ_X1000_H__ */

Some files were not shown because too many files have changed in this diff Show more