From 95f970076e9669c4d25021ac144fb21ac0aa2132 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 4 May 2025 21:50:30 -0400 Subject: [PATCH] surfansf28: Major improvements: * Add a crude keymap * Use native "hardware mute" for audiohw_mute() * Properly handle touchscreen inputs * Can now play back music, with some warts.. Broken: * rotary wheel still doesn't work * audio garbled/distorted a bit * no volume control Change-Id: I040217035a7bf3983b0e269fca3408eedd972cd0 --- apps/SOURCES | 2 + apps/keymaps/keymap-surfansf28.c | 225 ++++++++++++++++++ firmware/drivers/audio/surfanslinux_codec.c | 18 +- firmware/export/config/surfansf28.h | 5 +- firmware/target/hosted/surfans/button-f28.c | 2 + .../target/hosted/surfans/button-target.h | 2 + 6 files changed, 239 insertions(+), 15 deletions(-) create mode 100644 apps/keymaps/keymap-surfansf28.c diff --git a/apps/SOURCES b/apps/SOURCES index 1c454b3dae..bfad649847 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -301,4 +301,6 @@ keymaps/keymap-erosq.c keymaps/keymap-shanlingq1.c #elif CONFIG_KEYPAD == ECHO_R1_PAD keymaps/keymap-echor1.c +#elif CONFIG_KEYPAD == SURFANS_F28_PAD +keymaps/keymap-surfansf28.c #endif diff --git a/apps/keymaps/keymap-surfansf28.c b/apps/keymaps/keymap-surfansf28.c new file mode 100644 index 0000000000..21f3a044e0 --- /dev/null +++ b/apps/keymaps/keymap-surfansf28.c @@ -0,0 +1,225 @@ +/*************************************************************************** + * __________ __ ___ + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2025 Solomon Peachy + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include + +#include "config.h" +#include "action.h" +#include "button.h" +#include "settings.h" + +static const struct button_mapping button_context_standard[] = +{ + { ACTION_STD_CONTEXT, BUTTON_PLAY | BUTTON_REPEAT, BUTTON_PLAY }, + { ACTION_STD_OK, BUTTON_PLAY | BUTTON_REL, BUTTON_PLAY }, + { ACTION_STD_PREV, BUTTON_PREV, BUTTON_NONE }, + { ACTION_STD_NEXT, BUTTON_NEXT , BUTTON_NONE }, + { ACTION_STD_PREVREPEAT, BUTTON_PREV | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_NEXTREPEAT, BUTTON_NEXT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE }, + + LAST_ITEM_IN_LIST +}; + + +static const struct button_mapping button_context_wps[] = +{ + { ACTION_WPS_MENU, BUTTON_POWER, BUTTON_NONE }, + { ACTION_WPS_CONTEXT, BUTTON_PLAY | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_WPS_PLAY, BUTTON_PLAY | BUTTON_REL, BUTTON_NONE }, + { ACTION_WPS_SEEKBACK, BUTTON_PREV | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_WPS_SEEKFWD, BUTTON_NEXT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_WPS_STOPSEEK, BUTTON_PREV | BUTTON_REL, BUTTON_PREV | BUTTON_REPEAT }, + { ACTION_WPS_STOPSEEK, BUTTON_NEXT | BUTTON_REL, BUTTON_NEXT | BUTTON_REPEAT }, + { ACTION_WPS_SKIPNEXT, BUTTON_NEXT | BUTTON_REL, BUTTON_NONE }, + { ACTION_WPS_SKIPPREV, BUTTON_PREV | BUTTON_REL, BUTTON_NONE }, + { ACTION_WPS_VOLUP, BUTTON_RIGHT, BUTTON_NONE }, + { ACTION_WPS_VOLUP, BUTTON_RIGHT | BUTTON_REPEAT , BUTTON_NONE }, + { ACTION_WPS_VOLDOWN, BUTTON_LEFT, BUTTON_NONE }, + { ACTION_WPS_VOLDOWN, BUTTON_LEFT | BUTTON_REPEAT, BUTTON_NONE }, + + LAST_ITEM_IN_LIST +}; + + +static const struct button_mapping button_context_list[] = +{ + { ACTION_LIST_VOLUP, BUTTON_RIGHT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_LIST_VOLUP, BUTTON_RIGHT, BUTTON_NONE }, + { ACTION_LIST_VOLDOWN, BUTTON_LEFT, BUTTON_NONE }, + { ACTION_LIST_VOLDOWN, BUTTON_LEFT | BUTTON_REPEAT, BUTTON_NONE }, + + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), +}; + + +static const struct button_mapping button_context_tree[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST), +}; + + +static const struct button_mapping button_context_listtree_scroll_with_combo[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM | CONTEXT_TREE), +}; + + +static const struct button_mapping button_context_listtree_scroll_without_combo[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_CUSTOM | CONTEXT_TREE), +}; + + +static const struct button_mapping button_context_settings[] = +{ + { ACTION_SETTINGS_INC, BUTTON_RIGHT | BUTTON_REL, BUTTON_NONE }, + { ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_SETTINGS_DEC, BUTTON_LEFT | BUTTON_REL, BUTTON_NONE }, + { ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT | BUTTON_REPEAT, BUTTON_NONE }, + + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), +}; + + +static const struct button_mapping button_context_settings_right_is_inc[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), +}; + + +static const struct button_mapping button_context_mainmenu[] = +{ + { ACTION_TREE_WPS, BUTTON_POWER, BUTTON_NONE }, + + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_TREE), +}; + + +static const struct button_mapping button_context_yesno[] = +{ + { ACTION_YESNO_ACCEPT, BUTTON_PLAY, BUTTON_NONE }, + { ACTION_YESNO_ACCEPT, BUTTON_LEFT, BUTTON_NONE }, + + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), +}; + + +static const struct button_mapping button_context_colorchooser[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS), +}; + + +static const struct button_mapping button_context_eq[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS), +}; + + +static const struct button_mapping button_context_keyboard[] = +{ + { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE }, + { ACTION_KBD_LEFT, BUTTON_LEFT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_KBD_RIGHT, BUTTON_RIGHT, BUTTON_NONE }, + { ACTION_KBD_RIGHT, BUTTON_RIGHT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_KBD_CURSOR_LEFT, BUTTON_PLAY | BUTTON_LEFT, BUTTON_NONE }, + { ACTION_KBD_CURSOR_LEFT, BUTTON_PLAY | BUTTON_LEFT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_KBD_CURSOR_RIGHT, BUTTON_PLAY | BUTTON_RIGHT, BUTTON_NONE }, + { ACTION_KBD_CURSOR_RIGHT, BUTTON_PLAY | BUTTON_RIGHT | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_KBD_SELECT, BUTTON_PLAY, BUTTON_NONE }, + { ACTION_KBD_UP, BUTTON_PREV, BUTTON_NONE }, + { ACTION_KBD_UP, BUTTON_PREV | BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_KBD_DOWN, BUTTON_NEXT, BUTTON_NONE }, + { ACTION_KBD_DOWN, BUTTON_NEXT | BUTTON_REPEAT, BUTTON_NONE }, + + LAST_ITEM_IN_LIST +}; + + + +static const struct button_mapping button_context_bmark[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_LIST), +}; + + +static const struct button_mapping button_context_time[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS), +}; + + +static const struct button_mapping button_context_quickscreen[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; + + +static const struct button_mapping button_context_pitchscreen[] = +{ + { ACTION_PS_INC_SMALL, BUTTON_RIGHT | BUTTON_REL, BUTTON_NONE }, + { ACTION_PS_DEC_SMALL, BUTTON_LEFT |BUTTON_REL, BUTTON_NONE }, + { ACTION_PS_EXIT, BUTTON_POWER, BUTTON_NONE }, + + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; + + +static const struct button_mapping button_context_radio[] = +{ + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS) +}; + + +const struct button_mapping* target_get_context_mapping(int context) +{ + switch(context) + { + case CONTEXT_STD: { return button_context_standard; } + case CONTEXT_WPS: { return button_context_wps; } + case CONTEXT_LIST: { return button_context_list; } + case CONTEXT_MAINMENU: { return button_context_mainmenu; } + case CONTEXT_CUSTOM | CONTEXT_TREE: { return button_context_tree; } + case CONTEXT_SETTINGS: { return button_context_settings; } + case CONTEXT_SETTINGS_COLOURCHOOSER: { return button_context_colorchooser; } + case CONTEXT_SETTINGS_EQ: { return button_context_eq; } + case CONTEXT_SETTINGS_TIME: { return button_context_time; } + case CONTEXT_KEYBOARD: { return button_context_keyboard; } + case CONTEXT_FM: { return button_context_radio; } + case CONTEXT_BOOKMARKSCREEN: { return button_context_bmark; } + case CONTEXT_QUICKSCREEN: { return button_context_quickscreen; } + case CONTEXT_PITCHSCREEN: { return button_context_pitchscreen; } + case CONTEXT_CUSTOM | CONTEXT_SETTINGS: + case CONTEXT_SETTINGS_RECTRIGGER: { return button_context_settings_right_is_inc; } + case CONTEXT_TREE: + { + if(global_settings.hold_lr_for_scroll_in_list) + { + return button_context_listtree_scroll_without_combo; + } + return button_context_listtree_scroll_with_combo; + } + } + + return button_context_standard; +} diff --git a/firmware/drivers/audio/surfanslinux_codec.c b/firmware/drivers/audio/surfanslinux_codec.c index 786c41e36e..2a2edb4be5 100644 --- a/firmware/drivers/audio/surfanslinux_codec.c +++ b/firmware/drivers/audio/surfanslinux_codec.c @@ -84,23 +84,12 @@ static int muted = -1; void audiohw_mute(int mute) { - logf("mute %d", mute); - - if (!hw_init || muted == mute) + if (hw_init < 0 || muted == mute) return; muted = mute; - if(mute) - { - long int ps0 = 0; - alsa_controls_set_ints("Output Port Switch", 1, &ps0); - } - else - { - last_ps = 0; - surfans_get_outputs(); - } + alsa_controls_set_bool("Hardware Mute", !!mute); } int surfans_get_outputs(void){ @@ -144,7 +133,8 @@ void audiohw_preinit(void) alsa_controls_init("default"); hw_init = 1; - audiohw_mute(false); /* No need */ + audiohw_mute(false); /* No need ? */ + alsa_controls_set_bool("isDSD", 0); } void audiohw_postinit(void) diff --git a/firmware/export/config/surfansf28.h b/firmware/export/config/surfansf28.h index e1bc3d6952..de2e8dd906 100644 --- a/firmware/export/config/surfansf28.h +++ b/firmware/export/config/surfansf28.h @@ -17,9 +17,12 @@ //#define HAVE_QUICKSCREEN //#define HAVE_HOTKEY +#define HAVE_HEADPHONE_DETECTION + +#ifndef BOOTLOADER #define HAVE_BUTTON_DATA #define HAVE_TOUCHSCREEN -#define HAVE_HEADPHONE_DETECTION +#endif /* KeyPad configuration for plugins */ #define CONFIG_KEYPAD SURFANS_F28_PAD diff --git a/firmware/target/hosted/surfans/button-f28.c b/firmware/target/hosted/surfans/button-f28.c index 039f0466f5..81a8e1826e 100644 --- a/firmware/target/hosted/surfans/button-f28.c +++ b/firmware/target/hosted/surfans/button-f28.c @@ -47,6 +47,8 @@ int button_map(int keycode) return BUTTON_PREV; case KEY_POWER: return BUTTON_POWER; + case KEY_KPMINUS: + return BUTTON_TOUCH; default: return 0; } diff --git a/firmware/target/hosted/surfans/button-target.h b/firmware/target/hosted/surfans/button-target.h index 5b42827281..e59eee262e 100644 --- a/firmware/target/hosted/surfans/button-target.h +++ b/firmware/target/hosted/surfans/button-target.h @@ -32,6 +32,8 @@ #define BUTTON_RIGHT 0x00000020 #define BUTTON_MAIN 0x0000003f +#define BUTTON_TOUCH 0x00000040 + /* Touchscreen virtual buttons */ #define BUTTON_TOPLEFT 0x00001000 #define BUTTON_TOPMIDDLE 0x00002000