From 3e7a09cb0dee0ee04b5c77f427bf89d990ec8d0b Mon Sep 17 00:00:00 2001 From: Dana Conrad Date: Sat, 26 Jun 2021 12:07:11 -0500 Subject: [PATCH] New Port: Eros Q Native What works: - LCD: 16-bit RGB565 - all buttons, including scrollwheel - SD Card - Battery level and charging/not charging status - USB - audio - sample rate switching - HP / LO detect, with "safe" fixed LO volume - LO volume will only be put to user-defined max volume if headphones are not present. - rtc - Plugins build, tried a couple and they seem OK - Bootloader, installable to nand via usbboot What doesn't work: - Dual Boot - power on/off has intermittent, low volume audio click (sometimes it's completely silent, sometimes there's a click) - Audio uses 16-bit volume scaling, so clicking/popping is pretty bad at lower volumes - need 32 bit volume scaling, 24 bit I2S data - USB HID keys not yet defined - no jztool support Unknowns: - Stereo Switch pins: Direction select, AC_DC (probably not even hooked up) - What is the actual purpose of the Stereo Swtich? - How does the bluetooth module connect? "Someday" stuff: - get LCD working at higher bit depth - Bluetooth Change-Id: I70dda8fc092c6e3f4352f2245e4164193f803c33 --- apps/plugins/rockpaint.c | 11 + bootloader/SOURCES | 2 +- bootloader/x1000.c | 11 + firmware/SOURCES | 11 + firmware/drivers/audio/eros_qn_codec.c | 65 +++++ firmware/export/audiohw.h | 2 + firmware/export/config.h | 3 + firmware/export/config/erosqnative.h | 125 +++++++++ firmware/export/eros_qn_codec.h | 35 +++ .../target/mips/ingenic_x1000/clk-x1000.c | 4 +- .../ingenic_x1000/erosqnative/adc-target.h | 0 .../erosqnative/audiohw-erosqnative.c | 90 +++++++ .../erosqnative/backlight-erosqnative.c | 63 +++++ .../erosqnative/backlight-target.h | 33 +++ .../mips/ingenic_x1000/erosqnative/boot.make | 30 +++ .../erosqnative/button-erosqnative.c | 247 ++++++++++++++++++ .../ingenic_x1000/erosqnative/button-target.h | 45 ++++ .../ingenic_x1000/erosqnative/gpio-target.h | 71 +++++ .../ingenic_x1000/erosqnative/i2c-target.h | 31 +++ .../erosqnative/lcd-erosqnative.c | 188 +++++++++++++ .../erosqnative/power-erosqnative.c | 110 ++++++++ .../erosqnative/spl-erosqnative.c | 63 +++++ .../target/mips/ingenic_x1000/lcd-x1000.c | 4 + .../target/mips/ingenic_x1000/msc-x1000.c | 10 + .../target/mips/ingenic_x1000/nand-x1000.c | 2 +- .../target/mips/ingenic_x1000/spl-x1000.c | 4 + tools/configure | 26 +- tools/scramble.c | 2 + 28 files changed, 1283 insertions(+), 5 deletions(-) create mode 100644 firmware/drivers/audio/eros_qn_codec.c create mode 100644 firmware/export/config/erosqnative.h create mode 100644 firmware/export/eros_qn_codec.h create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/boot.make create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/button-target.h create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c create mode 100644 firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c diff --git a/apps/plugins/rockpaint.c b/apps/plugins/rockpaint.c index 98fdb468d3..a0d9b26ec2 100644 --- a/apps/plugins/rockpaint.c +++ b/apps/plugins/rockpaint.c @@ -407,6 +407,17 @@ #elif CONFIG_KEYPAD == SHANLING_Q1_PAD /* use touchscreen */ +#elif CONFIG_KEYPAD == EROSQ_PAD +#define ROCKPAINT_QUIT BUTTON_POWER +#define ROCKPAINT_DRAW BUTTON_PLAY +#define ROCKPAINT_MENU BUTTON_MENU +#define ROCKPAINT_TOOLBAR BUTTON_VOL_UP +#define ROCKPAINT_TOOLBAR2 BUTTON_VOL_DOWN +#define ROCKPAINT_UP BUTTON_PREV +#define ROCKPAINT_DOWN BUTTON_NEXT +#define ROCKPAINT_LEFT BUTTON_SCROLL_BACK +#define ROCKPAINT_RIGHT BUTTON_SCROLL_FWD + #else #error "Please define keys for this keypad" #endif diff --git a/bootloader/SOURCES b/bootloader/SOURCES index 6cdcd41ece..86dd64d7d9 100644 --- a/bootloader/SOURCES +++ b/bootloader/SOURCES @@ -89,6 +89,6 @@ show_logo.c #elif defined(SANSA_CONNECT) sansaconnect.c show_logo.c -#elif defined(FIIO_M3K) || defined(SHANLING_Q1) +#elif defined(FIIO_M3K) || defined(SHANLING_Q1) || defined(EROS_QN) x1000.c #endif diff --git a/bootloader/x1000.c b/bootloader/x1000.c index 3467547684..a7c7692ac8 100644 --- a/bootloader/x1000.c +++ b/bootloader/x1000.c @@ -77,6 +77,17 @@ # define BL_SELECT_NAME "PLAY" # define BL_QUIT_NAME "POWER" # define BOOTBACKUP_FILE "/shanlingq1-boot.bin" +#elif defined(EROS_QN) +# define BL_RECOVERY BUTTON_VOL_UP +# define BL_UP BUTTON_SCROLL_BACK +# define BL_DOWN BUTTON_SCROLL_FWD +# define BL_SELECT BUTTON_PLAY +# define BL_QUIT BUTTON_POWER +# define BL_UP_NAME "Up" +# define BL_DOWN_NAME "Scroll Down" +# define BL_SELECT_NAME "PLAY" +# define BL_QUIT_NAME "POWER" +# define BOOTBACKUP_FILE "/erosqnative-boot.bin" #else # error "Missing keymap!" #endif diff --git a/firmware/SOURCES b/firmware/SOURCES index e055558b57..fc194fe640 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -505,6 +505,8 @@ drivers/audio/cs4398.c drivers/audio/es9018.c #elif defined (HAVE_ES9218) drivers/audio/es9218.c +#elif defined (HAVE_EROS_QN_CODEC) +drivers/audio/eros_qn_codec.c #endif /* defined(HAVE_*) */ #else /* PLATFORM_HOSTED */ #if defined(SAMSUNG_YPR0) && defined(HAVE_AS3514) @@ -1727,6 +1729,15 @@ target/mips/ingenic_x1000/shanlingq1/power-shanlingq1.c target/mips/ingenic_x1000/shanlingq1/spl-shanlingq1.c #endif /* SHANLING_Q1 */ +#if defined(EROS_QN) +target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c +target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c +target/mips/ingenic_x1000/erosqnative/button-erosqnative.c +target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c +target/mips/ingenic_x1000/erosqnative/power-erosqnative.c +target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c +#endif /* EROS_QN */ + #if defined(LYRE_PROTO1) target/arm/at91sam/lyre_proto1/adc-lyre_proto1.c target/arm/at91sam/lyre_proto1/backlight-lyre_proto1.c diff --git a/firmware/drivers/audio/eros_qn_codec.c b/firmware/drivers/audio/eros_qn_codec.c new file mode 100644 index 0000000000..21347f5fca --- /dev/null +++ b/firmware/drivers/audio/eros_qn_codec.c @@ -0,0 +1,65 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Andrew Ryabinin, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "system.h" +#include "eros_qn_codec.h" +#include "config.h" +#include "audio.h" +#include "audiohw.h" +#include "settings.h" +#include "pcm_sw_volume.h" + +static long int vol_l_hw = 0; +static long int vol_r_hw = 0; + +void pcm5102_set_outputs(void) +{ + audiohw_set_volume(vol_l_hw, vol_r_hw); +} + +/* this makes less sense here than it does in the audiohw-*.c file, + * but we need access to settings.h */ +void audiohw_set_volume(int vol_l, int vol_r) +{ + int l, r; + + vol_l_hw = vol_l; + vol_r_hw = vol_r; + + l = vol_l; + r = vol_r; + +#if (defined(HAVE_HEADPHONE_DETECTION) && defined(HAVE_LINEOUT_DETECTION)) + /* make sure headphones aren't present - don't want to + * blow out our eardrums cranking it to full */ + if (lineout_inserted() && !headphones_inserted()) + { + l = r = global_settings.volume_limit * 10; + } + else + { + l = vol_l; + r = vol_r; + } +#endif + + pcm_set_master_volume(l, r); +} diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h index 5b2815149d..5a706da3b8 100644 --- a/firmware/export/audiohw.h +++ b/firmware/export/audiohw.h @@ -210,6 +210,8 @@ struct sound_settings_info #include "df1704.h" #elif defined(HAVE_PCM1792_CODEC) #include "pcm1792.h" +#elif defined(HAVE_EROS_QN_CODEC) +#include "eros_qn_codec.h" #elif defined(HAVE_NWZ_LINUX_CODEC) #include "nwzlinux_codec.h" #elif defined(HAVE_CS4398) diff --git a/firmware/export/config.h b/firmware/export/config.h index 5e7b2be6e4..702b821856 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -276,6 +276,7 @@ #define LCD_IHIFI800 68 /* as used by IHIFI 800 */ #define LCD_FIIOM3K 69 /* as used by the FiiO M3K */ #define LCD_SHANLING_Q1 70 /* as used by the Shanling Q1 */ +#define LCD_EROSQ 71 /* as used by the ErosQ (native) */ /* LCD_PIXELFORMAT */ #define HORIZONTAL_PACKING 1 @@ -596,6 +597,8 @@ Lyre prototype 1 */ #include "config/aigoerosq.h" #elif defined(SHANLING_Q1) #include "config/shanlingq1.h" +#elif defined(EROS_QN) +#include "config/erosqnative.h" #else //#error "unknown hwardware platform!" #endif diff --git a/firmware/export/config/erosqnative.h b/firmware/export/config/erosqnative.h new file mode 100644 index 0000000000..1b37b6042a --- /dev/null +++ b/firmware/export/config/erosqnative.h @@ -0,0 +1,125 @@ +/* + * This config file is for the AIGO EROS Q / EROS K (and its clones) + */ + +/* For Rolo and boot loader */ +#define MODEL_NAME "AIGO Eros Q Native" +#define MODEL_NUMBER 116 +#define BOOTFILE_EXT "erosq" +#define BOOTFILE "rockbox." BOOTFILE_EXT +#define BOOTDIR "/.rockbox" +#define FIRMWARE_OFFSET_FILE_CRC 0 +#define FIRMWARE_OFFSET_FILE_DATA 8 + +/* CPU defines */ +#define CONFIG_CPU X1000 +#define X1000_EXCLK_FREQ 24000000 +#define CPU_FREQ 1008000000 +#define HAVE_FPU + +#ifndef SIMULATOR +#define TIMER_FREQ X1000_EXCLK_FREQ +#endif + +/* kernel defines */ +#define INCLUDE_TIMEOUT_API +#define HAVE_SEMAPHORE_OBJECTS + +/* drivers */ +#define HAVE_I2C_ASYNC + +/* Buffers for plugsins and codecs */ +#define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */ +#define CODEC_SIZE 0x100000 /* 1 MiB */ + +/* LCD defines */ +#define CONFIG_LCD LCD_EROSQ +#define LCD_WIDTH 320 +#define LCD_HEIGHT 240 +#define LCD_DEPTH 16 /* Future Improvement: 18 or 24 bpp if display supports it */ +#define LCD_PIXELFORMAT RGB565 +/* sqrt(240^2 + 320^2) / 2.0 = 200 */ +#define LCD_DPI 200 +#define HAVE_LCD_COLOR +#define HAVE_LCD_BITMAP +#define HAVE_LCD_ENABLE +#define HAVE_LCD_SHUTDOWN +#define LCD_X1000_FASTSLEEP +//#define LCD_X1000_DMA_WAITFORFRAME + +#define HAVE_BACKLIGHT +#define HAVE_BACKLIGHT_BRIGHTNESS +#define MIN_BRIGHTNESS_SETTING 1 +#define MAX_BRIGHTNESS_SETTING 255 +#define BRIGHTNESS_STEP 5 +#define DEFAULT_BRIGHTNESS_SETTING 70 +#define CONFIG_BACKLIGHT_FADING BACKLIGHT_FADING_SW_SETTING + +/* RTC settings */ +#define CONFIG_RTC RTC_X1000 + +/* Codec / audio hardware defines */ +#define HW_SAMPR_CAPS SAMPR_CAP_ALL_192 +#define HAVE_EROS_QN_CODEC +#define HAVE_SW_TONE_CONTROLS +#define HAVE_SW_VOLUME_CONTROL + +/* Button defines */ +#define CONFIG_KEYPAD EROSQ_PAD +#define HAVE_SCROLLWHEEL +#define HAVE_HEADPHONE_DETECTION +#define HAVE_LINEOUT_DETECTION + +/* Storage defines */ +#define CONFIG_STORAGE STORAGE_SD +#define HAVE_HOTSWAP +#define HAVE_HOTSWAP_STORAGE_AS_MAIN +#define HAVE_MULTIDRIVE +#define NUM_DRIVES 1 +#define STORAGE_WANTS_ALIGN +#define STORAGE_NEEDS_BOUNCE_BUFFER + +/* Power management */ +#define CONFIG_BATTERY_MEASURE VOLTAGE_MEASURE +#define CONFIG_CHARGING CHARGING_MONITOR +#define HAVE_SW_POWEROFF + +#ifndef SIMULATOR +#define HAVE_AXP_PMU 192 +#define HAVE_POWEROFF_WHILE_CHARGING +#endif + +/* Battery */ +#define BATTERY_TYPES_COUNT 1 +#define BATTERY_CAPACITY_DEFAULT 1300 /* default battery capacity */ +#define BATTERY_CAPACITY_MIN 1300 /* min. capacity selectable */ +#define BATTERY_CAPACITY_MAX 1300 /* max. capacity selectable */ +#define BATTERY_CAPACITY_INC 0 /* capacity increment */ + +#define CURRENT_NORMAL 100 // 1.7mA * 60s +#define CURRENT_BACKLIGHT 180 +#define CURRENT_MAX_CHG 500 // bursts higher if needed + +/* USB support */ +#ifndef SIMULATOR +#define CONFIG_USBOTG USBOTG_DESIGNWARE +#define USB_DW_ARCH_SLAVE +#define USB_DW_TURNAROUND 5 +#define HAVE_USBSTACK +#define USB_VENDOR_ID 0xc502 +#define USB_PRODUCT_ID 0x0023 +#define USB_DEVBSS_ATTR __attribute__((aligned(32))) +#define HAVE_USB_POWER +#define HAVE_USB_CHARGING_ENABLE +#define HAVE_BOOTLOADER_USB_MODE +#endif + +/* Rockbox capabilities */ +#define HAVE_VOLUME_IN_LIST +#define HAVE_FAT16SUPPORT +#define HAVE_ALBUMART +#define HAVE_BMP_SCALING +#define HAVE_JPEG +#define HAVE_TAGCACHE +#define HAVE_QUICKSCREEN +#define HAVE_HOTKEY diff --git a/firmware/export/eros_qn_codec.h b/firmware/export/eros_qn_codec.h new file mode 100644 index 0000000000..15c745c04b --- /dev/null +++ b/firmware/export/eros_qn_codec.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * + * Copyright (c) 2021 Andrew Ryabinin, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef _EROS_QN_CODEC_H +#define _EROS_QN_CODEC_H + +#define PCM5102A_VOLUME_MIN -740 +#define PCM5102A_VOLUME_MAX 0 + +AUDIOHW_SETTING(VOLUME, "dB", 0, 1, PCM5102A_VOLUME_MIN/10, PCM5102A_VOLUME_MAX/10, 0) + +/* this just calls audiohw_set_volume() with the last (locally) known volume, + * used for switching to/from fixed line out volume. */ +void pcm5102_set_outputs(void); + +#endif diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c index 7e254401fb..4988e7c3bf 100644 --- a/firmware/target/mips/ingenic_x1000/clk-x1000.c +++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c @@ -265,7 +265,7 @@ void clk_init(void) jz_writef(CPM_APCR, BS(1), PLLM(42 - 1), PLLN(0), PLLOD(0), ENABLE(1)); while(jz_readf(CPM_APCR, ON) == 0); -#if defined(FIIO_M3K) +#if (defined(FIIO_M3K) || defined(EROS_QN)) /* TODO: Allow targets to define their clock frequencies in their config, * instead of having this be a random special case. */ if(get_boot_option() == BOOT_OPTION_ROCKBOX) { @@ -296,7 +296,7 @@ void clk_init(void) CLKMUX_CPU(SCLK_A) | CLKMUX_AHB0(MPLL) | CLKMUX_AHB2(MPLL)); -#if defined(FIIO_M3K) +#if (defined(FIIO_M3K) || defined(EROS_QN)) } #endif diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c new file mode 100644 index 0000000000..7bb985650c --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/audiohw-erosqnative.c @@ -0,0 +1,90 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "audiohw.h" +#include "system.h" +#include "pcm_sampr.h" +#include "aic-x1000.h" +#include "i2c-x1000.h" +#include "gpio-x1000.h" +#include "logf.h" + +/* Audio path appears to be: + * DAC --> HP Amp --> Stereo Switch --> HP OUT + * \--> LO OUT + * + * The real purpose of the Stereo Switch is not clear. + * It appears to switch sources between the HP amp and something, + * likely something unimplemented. */ + +void audiohw_init(void) +{ + /* explicitly mute everything */ + gpio_set_level(GPIO_MAX97220_SHDN, 0); + gpio_set_level(GPIO_ISL54405_MUTE, 1); + gpio_set_level(GPIO_PCM5102A_XMIT, 0); + + aic_set_external_codec(true); + aic_set_i2s_mode(AIC_I2S_MASTER_MODE); + audiohw_set_frequency(HW_FREQ_48); + + aic_enable_i2s_master_clock(true); + aic_enable_i2s_bit_clock(true); + + mdelay(10); + + /* power on DAC and HP Amp */ + gpio_set_level(GPIO_PCM5102A_ANALOG_PWR, 1); + gpio_set_level(GPIO_MAX97220_POWER, 1); +} + +void audiohw_postinit(void) +{ + /* unmute - attempt to make power-on pop-free */ + gpio_set_level(GPIO_ISL54405_SEL, 0); + gpio_set_level(GPIO_MAX97220_SHDN, 1); + mdelay(10); + gpio_set_level(GPIO_PCM5102A_XMIT, 1); + mdelay(10); + gpio_set_level(GPIO_ISL54405_MUTE, 0); +} + +/* TODO: get shutdown just right according to dac datasheet */ +void audiohw_close(void) +{ + /* mute - attempt to make power-off pop-free */ + gpio_set_level(GPIO_ISL54405_MUTE, 1); + mdelay(10); + gpio_set_level(GPIO_PCM5102A_XMIT, 0); + mdelay(10); + gpio_set_level(GPIO_MAX97220_SHDN, 0); +} + +void audiohw_set_frequency(int fsel) +{ + int sampr = hw_freq_sampr[fsel]; + int mult = 256; + + aic_enable_i2s_bit_clock(false); + aic_set_i2s_clock(X1000_CLK_SCLK_A, sampr, mult); + aic_enable_i2s_bit_clock(true); +} + diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c new file mode 100644 index 0000000000..2c86a995db --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-erosqnative.c @@ -0,0 +1,63 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "backlight.h" +#include "backlight-target.h" +#include "lcd.h" +#include "pwm-x1000.h" + +#define BL_LCD_CHN 0 +#define BL_LCD_PERIOD 30000 + +static int backlight_calc_duty(int period, int min_duty, int brightness) +{ + return min_duty + (period - min_duty) * brightness / MAX_BRIGHTNESS_SETTING; +} + +bool backlight_hw_init(void) +{ + pwm_init(BL_LCD_CHN); + pwm_enable(BL_LCD_CHN); + backlight_hw_brightness(MAX_BRIGHTNESS_SETTING); + return true; +} + +void backlight_hw_on(void) +{ + pwm_enable(BL_LCD_CHN); +#ifdef HAVE_LCD_ENABLE + lcd_enable(true); +#endif +} + +void backlight_hw_off(void) +{ + pwm_disable(BL_LCD_CHN); +#ifdef HAVE_LCD_ENABLE + lcd_enable(false); +#endif +} + +void backlight_hw_brightness(int brightness) +{ + int duty_ns = backlight_calc_duty(BL_LCD_PERIOD, 0, brightness); + pwm_set_period(BL_LCD_CHN, BL_LCD_PERIOD, duty_ns); +} diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h new file mode 100644 index 0000000000..05d6e36679 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/backlight-target.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __BACKLIGHT_TARGET_H__ +#define __BACKLIGHT_TARGET_H__ + +#include + +extern bool backlight_hw_init(void); + +extern void backlight_hw_on(void); +extern void backlight_hw_off(void); +extern void backlight_hw_brightness(int brightness); + +#endif /* __BACKLIGHT_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/boot.make b/firmware/target/mips/ingenic_x1000/erosqnative/boot.make new file mode 100644 index 0000000000..b6d0844cd0 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/boot.make @@ -0,0 +1,30 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $Id$ +# + +include $(ROOTDIR)/lib/microtar/microtar.make + +.SECONDEXPANSION: + +$(BUILDDIR)/spl.erosq: $(BUILDDIR)/spl.bin + $(call PRINTS,MKSPL $(@F))$(TOOLSDIR)/mkspl-x1000 -type=nand -ppb=2 -bpp=2 $< $@ + +$(BUILDDIR)/bootloader.ucl: $(BUILDDIR)/bootloader.bin + $(call PRINTS,UCLPACK $(@F))$(TOOLSDIR)/uclpack --nrv2e -9 $< $@ >/dev/null + +.PHONY: $(BUILDDIR)/bootloader-info.txt +$(BUILDDIR)/bootloader-info.txt: + $(call PRINTS,GEN $(@F))echo $(SVNVERSION) > $@ + +$(BUILDDIR)/$(BINARY): $(BUILDDIR)/spl.erosq \ + $(BUILDDIR)/bootloader.ucl \ + $(BUILDDIR)/bootloader-info.txt + $(call PRINTS,TAR $(@F))tar -C $(BUILDDIR) \ + --numeric-owner --no-acls --no-xattrs --no-selinux \ + --mode=0644 --owner=0 --group=0 \ + -cf $@ $(call full_path_subst,$(BUILDDIR)/%,%,$^) diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c new file mode 100644 index 0000000000..1583db175a --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-erosqnative.c @@ -0,0 +1,247 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "button.h" +#include "kernel.h" +#include "backlight.h" +#include "powermgmt.h" +#include "panic.h" +#include "axp-pmu.h" +#include "gpio-x1000.h" +#include "irq-x1000.h" +#include "i2c-x1000.h" +#include "eros_qn_codec.h" +#include +#include + +#ifndef BOOTLOADER +# include "lcd.h" +# include "font.h" +#endif + +/* =========================================== + * | OLD STATE | NEW STATE | DIRECTION | + * | 0 0 | 0 0 | 0: NO CHANGE | + * | 0 0 | 0 1 | -1: CCW | + * | 0 0 | 1 0 | 1: CW | + * | 0 0 | 1 1 | 0: INVALID | + * | 0 1 | 0 0 | 1: CW | + * | 0 1 | 0 1 | 0: NO CHANGE | + * | 0 1 | 1 0 | 0: INVALID | + * | 0 1 | 1 1 | -1: CCW | + * | 1 0 | 0 0 | -1: CCW | + * | 1 0 | 0 1 | 0: INVALID | + * | 1 0 | 1 0 | 0: NO CHANGE | + * | 1 0 | 1 1 | 1: CW | + * | 1 1 | 0 0 | 0: INVALID | + * | 1 1 | 0 1 | 1: CW | + * | 1 1 | 1 0 | -1: CCW | + * | 1 1 | 1 1 | 0: NO CHANGE | + * =========================================== + * + * Quadrature explanation since it's not plainly obvious how this works: + * + * If either of the quadrature lines change, we can look up the combination + * of previous state and new state in the table above (enc_state[] below) + * and it tells us whether to add 1, subtract 1, or no change from the sum (enc_position). + * This also gives us a nice debounce, since each state can only have 1 pin change + * at a time. I didn't come up with this, but I've used it before and it works well. + * + * Old state is 2 higher bits, new state is 2 lower bits of enc_current_state. */ + +/* list of valid quadrature states and their directions */ +signed char enc_state[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0}; +volatile unsigned char enc_current_state = 0; +volatile signed int enc_position = 0; + +/* Value of headphone detect register */ +static uint8_t hp_detect_reg = 0x00; +static uint8_t hp_detect_reg_old = 0x00; + +/* Interval to poll the register */ +#define HPD_POLL_TIME (HZ/2) + +static int hp_detect_tmo_cb(struct timeout* tmo) +{ + i2c_descriptor* d = (i2c_descriptor*)tmo->data; + i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); + return HPD_POLL_TIME; +} + +static void hp_detect_init(void) +{ + static struct timeout tmo; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; + static i2c_descriptor desc = { + .slave_addr = AXP_PMU_ADDR, + .bus_cond = I2C_START | I2C_STOP, + .tran_mode = I2C_READ, + .buffer[0] = (void*)&gpio_reg, + .count[0] = 1, + .buffer[1] = &hp_detect_reg, + .count[1] = 1, + .callback = NULL, + .arg = 0, + .next = NULL, + }; + + /* Headphone and LO detects are wired to AXP192 GPIOs 0 and 1, + * set them to inputs. */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO0FUNCTION, 0x01); /* HP detect */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); /* LO detect */ + + /* Get an initial reading before startup */ + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + if(r >= 0) + { + hp_detect_reg = r; + hp_detect_reg_old = hp_detect_reg; + } + + /* Poll the register every second */ + timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc); +} + +bool headphones_inserted(void) +{ + /* if the status has changed, set the output volume accordingly */ + if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30)) + { + hp_detect_reg_old = hp_detect_reg; +#if !defined(BOOTLOADER) + pcm5102_set_outputs(); +#endif + } + return hp_detect_reg & 0x10 ? false : true; +} + +bool lineout_inserted(void) +{ + /* if the status has changed, set the output volume accordingly */ + if ((hp_detect_reg & 0x30) != (hp_detect_reg_old & 0x30)) + { + hp_detect_reg_old = hp_detect_reg; +#if !defined(BOOTLOADER) + pcm5102_set_outputs(); +#endif + } + return hp_detect_reg & 0x20 ? false : true; +} + +/* Rockbox interface */ +void button_init_device(void) +{ + /* set both quadrature lines to interrupts */ + gpio_set_function(GPIO_BTN_SCROLL_A, GPIOF_IRQ_EDGE(1)); + gpio_set_function(GPIO_BTN_SCROLL_B, GPIOF_IRQ_EDGE(1)); + + /* set interrupts to fire on the next edge based on current state */ + gpio_flip_edge_irq(GPIO_BTN_SCROLL_A); + gpio_flip_edge_irq(GPIO_BTN_SCROLL_B); + + /* get current state of both encoder gpios */ + enc_current_state = (REG_GPIO_PIN(GPIO_B)>>21) & 0x0c; + + /* enable quadrature interrupts */ + gpio_enable_irq(GPIO_BTN_SCROLL_A); + gpio_enable_irq(GPIO_BTN_SCROLL_B); + + /* Set up headphone and line out detect polling */ + hp_detect_init(); +} + +/* wheel Quadrature line A interrupt */ +void GPIOB24(void) +{ + /* fill state with previous (2 higher bits) and current (2 lower bits) */ + enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03); + + /* look up in table */ + enc_position = enc_position + enc_state[(enc_current_state)]; + + /* move current state to previous state if valid data */ + if (enc_state[(enc_current_state)] != 0) + enc_current_state = (enc_current_state << 2); + + /* we want the other edge next time */ + gpio_flip_edge_irq(GPIO_BTN_SCROLL_A); +} + +/* wheel Quadrature line B interrupt */ +void GPIOB23(void) +{ + /* fill state with previous (2 higher bits) and current (2 lower bits) */ + enc_current_state = (enc_current_state & 0x0c) | ((REG_GPIO_PIN(GPIO_B)>>23) & 0x03); + + /* look up in table */ + enc_position = enc_position + enc_state[(enc_current_state)]; + + /* move current state to previous state if valid data */ + if (enc_state[(enc_current_state)] != 0) + enc_current_state = (enc_current_state << 2); + + /* we want the other edge next time */ + gpio_flip_edge_irq(GPIO_BTN_SCROLL_B); +} + +int button_read_device(void) +{ + int r = 0; + + /* Read GPIOs for normal buttons */ + uint32_t a = REG_GPIO_PIN(GPIO_A); + uint32_t b = REG_GPIO_PIN(GPIO_B); + uint32_t c = REG_GPIO_PIN(GPIO_C); + uint32_t d = REG_GPIO_PIN(GPIO_D); + + /* All buttons are active low */ + if((a & (1 << 16)) == 0) r |= BUTTON_PLAY; + if((a & (1 << 17)) == 0) r |= BUTTON_VOL_UP; + if((a & (1 << 19)) == 0) r |= BUTTON_VOL_DOWN; + + if((b & (1 << 7)) == 0) r |= BUTTON_POWER; + if((b & (1 << 28)) == 0) r |= BUTTON_MENU; + if((b & (1 << 28)) == 0) r |= BUTTON_MENU; + + if((d & (1 << 4)) == 0) r |= BUTTON_PREV; + + if((d & (1 << 5)) == 0) r |= BUTTON_BACK; + if((c & (1 << 24)) == 0) r |= BUTTON_NEXT; + + /* check encoder - from testing, each indent is 2 state changes or so */ + if (enc_position > 1) + { + /* need to use queue_post() in order to do BUTTON_SCROLL_*, + * Rockbox treats these buttons differently. */ + queue_post(&button_queue, BUTTON_SCROLL_FWD, 0); + enc_position = 0; + } + else if (enc_position < -1) + { + /* need to use queue_post() in order to do BUTTON_SCROLL_*, + * Rockbox treats these buttons differently. */ + queue_post(&button_queue, BUTTON_SCROLL_BACK, 0); + enc_position = 0; + } + + return r; +} + diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h new file mode 100644 index 0000000000..9c39a40296 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/button-target.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2021 Solomon Peachy, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _BUTTON_TARGET_H_ +#define _BUTTON_TARGET_H_ + +/* Main unit's buttons */ +#define BUTTON_POWER 0x00000001 +#define BUTTON_MENU 0x00000002 +#define BUTTON_BACK 0x00000004 +#define BUTTON_PLAY 0x00000008 +#define BUTTON_NEXT 0x00000010 +#define BUTTON_PREV 0x00000020 +#define BUTTON_VOL_UP 0x00000040 +#define BUTTON_VOL_DOWN 0x00000080 +#define BUTTON_SCROLL_BACK 0x00000100 +#define BUTTON_SCROLL_FWD 0x00000200 + +#define BUTTON_MAIN (BUTTON_POWER | BUTTON_MENU | BUTTON_BACK | BUTTON_PREV | \ + BUTTON_NEXT | BUTTON_PLAY | BUTTON_VOL_UP | BUTTON_VOL_DOWN | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD) + +#define BUTTON_LEFT BUTTON_PREV +#define BUTTON_RIGHT BUTTON_NEXT + +/* Software power-off */ +#define POWEROFF_BUTTON BUTTON_POWER +#define POWEROFF_COUNT 25 + +#endif /* _BUTTON_TARGET_H_ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h new file mode 100644 index 0000000000..376eae136e --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/gpio-target.h @@ -0,0 +1,71 @@ +/* -------------------- NOTES ------------------- */ + +/* I don't think we have any devices on I2C1, the pins /may/ be reused. */ +/* DEFINE_PINGROUP(I2C1, GPIO_C, 3 << 26, GPIOF_DEVICE(0)) */ + +/* OF has SD Card power listed as 0x2a - PB10, but it seems to work without. */ + +/* I think BT power reg is pin 0x53 - C19 */ + +/* USB_DETECT D3 chosen by trial-and-error. */ + +/* I have a suspicion this isn't right for AXP_IRQ, + * and it's not used right now anyway. copied from m3k. */ +/* DEFINE_GPIO(AXP_IRQ, GPIO_PB(10), GPIOF_INPUT) */ + +/* ---------------------------------------------- */ + +/* Name Port Pins Function */ +DEFINE_PINGROUP(LCD_DATA, GPIO_A, 0xffff << 0, GPIOF_DEVICE(1)) +DEFINE_PINGROUP(LCD_CONTROL, GPIO_B, 0x1a << 16, GPIOF_DEVICE(1)) +DEFINE_PINGROUP(MSC0, GPIO_A, 0x3f << 20, GPIOF_DEVICE(1)) +DEFINE_PINGROUP(SFC, GPIO_A, 0x3f << 26, GPIOF_DEVICE(1)) +DEFINE_PINGROUP(I2S, GPIO_B, 0x1f << 0, GPIOF_DEVICE(1)) +DEFINE_PINGROUP(I2C2, GPIO_D, 3 << 0, GPIOF_DEVICE(1)) + +/* Name Pin Function */ +/* mute DAC - 0 - mute, 1 - play. Affects both HP and LO. */ +DEFINE_GPIO(PCM5102A_XMIT, GPIO_PB(12), GPIOF_OUTPUT(0)) + +/* mute HP amp, no effect on LO. 0 - mute, 1 - play */ +DEFINE_GPIO(MAX97220_SHDN, GPIO_PB(8), GPIOF_OUTPUT(0)) + +/* mute audio mux, only affects Headphone out. + * 0 - play, 1 - mute */ +DEFINE_GPIO(ISL54405_MUTE, GPIO_PB(15), GPIOF_OUTPUT(1)) + +/* switches HP on/off - 0 HP on, 1 hp off, has no effect on LO. + * As best I can tell, it switches HP Out sources between HP amp and something + * not implemented - there seem to be resistors missing. */ +DEFINE_GPIO(ISL54405_SEL, GPIO_PB(5), GPIOF_OUTPUT(0)) + +/* DAC AVDD */ +DEFINE_GPIO(PCM5102A_ANALOG_PWR, GPIO_PB(9), GPIOF_OUTPUT(0)) + +/* Headphone Amp power */ +DEFINE_GPIO(MAX97220_POWER, GPIO_PB(6), GPIOF_OUTPUT(0)) + +/* SD card */ +DEFINE_GPIO(MSC0_CD, GPIO_PB(11), GPIOF_INPUT) + +/* USB */ +DEFINE_GPIO(USB_DETECT, GPIO_PD(3), GPIOF_INPUT) +DEFINE_GPIO(USB_DRVVBUS, GPIO_PB(25), GPIOF_OUTPUT(0)) + +/* LCD */ +DEFINE_GPIO(LCD_PWR, GPIO_PB(14), GPIOF_OUTPUT(0)) +DEFINE_GPIO(LCD_RESET, GPIO_PB(13), GPIOF_OUTPUT(0)) +DEFINE_GPIO(LCD_CE, GPIO_PB(18), GPIOF_OUTPUT(1)) +DEFINE_GPIO(LCD_RD, GPIO_PB(16), GPIOF_OUTPUT(1)) + +/* Buttons */ +DEFINE_GPIO(BTN_PLAY, GPIO_PA(16), GPIOF_INPUT) +DEFINE_GPIO(BTN_VOL_UP, GPIO_PA(17), GPIOF_INPUT) +DEFINE_GPIO(BTN_VOL_DOWN, GPIO_PA(19), GPIOF_INPUT) +DEFINE_GPIO(BTN_POWER, GPIO_PB(7), GPIOF_INPUT) +DEFINE_GPIO(BTN_MENU, GPIO_PB(28), GPIOF_INPUT) +DEFINE_GPIO(BTN_BACK, GPIO_PD(5), GPIOF_INPUT) +DEFINE_GPIO(BTN_PREV, GPIO_PD(4), GPIOF_INPUT) +DEFINE_GPIO(BTN_NEXT, GPIO_PC(24), GPIOF_INPUT) +DEFINE_GPIO(BTN_SCROLL_A, GPIO_PB(24), GPIOF_INPUT) +DEFINE_GPIO(BTN_SCROLL_B, GPIO_PB(23), GPIOF_INPUT) diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h new file mode 100644 index 0000000000..8d0b8a6e20 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/i2c-target.h @@ -0,0 +1,31 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __I2C_TARGET_H__ +#define __I2C_TARGET_H__ + +#define I2C_ASYNC_BUS_COUNT 3 +#define I2C_ASYNC_QUEUE_SIZE 4 + +#define AXP_PMU_BUS 2 +#define AXP_PMU_ADDR 0x34 + +#endif /* __I2C_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c new file mode 100644 index 0000000000..073bddb8b4 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/lcd-erosqnative.c @@ -0,0 +1,188 @@ + +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "lcd.h" +#include "kernel.h" +#include "lcd-x1000.h" +#include "gpio-x1000.h" +#include "system.h" + +/* for reference on these command/data hex values, see the mipi dcs lcd spec. * + * Not everything here is there, but all the standard stuff is. */ + +static const uint32_t erosqnative_lcd_cmd_enable[] = { + /* Set EXTC? */ + LCD_INSTR_CMD, 0xc8, + LCD_INSTR_DAT, 0xff, + LCD_INSTR_DAT, 0x93, + LCD_INSTR_DAT, 0x42, + /* Set Address Mode */ + LCD_INSTR_CMD, 0x36, + LCD_INSTR_DAT, 0xd8, + /* Pixel Format Set */ + LCD_INSTR_CMD, 0x3a, + //LCD_INSTR_DAT, 0x66, /* OF specified 18 bpp */ + LCD_INSTR_DAT, 0x05, /* RB seems to be happier dealing with 16 bits/pixel */ + /* Power Control 1? */ + LCD_INSTR_CMD, 0xc0, + LCD_INSTR_DAT, 0x15, + LCD_INSTR_DAT, 0x15, + /* Power Control 2? */ + LCD_INSTR_CMD, 0xc1, + LCD_INSTR_DAT, 0x01, + /* VCOM? */ + LCD_INSTR_CMD, 0xc5, + LCD_INSTR_DAT, 0xda, + /* ?? */ + LCD_INSTR_CMD, 0xb1, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x1b, + /* ?? */ + LCD_INSTR_CMD, 0xb4, + LCD_INSTR_DAT, 0x02, + /* Positive gamma correction? */ + LCD_INSTR_CMD, 0xe0, + LCD_INSTR_DAT, 0x0f, + LCD_INSTR_DAT, 0x13, + LCD_INSTR_DAT, 0x17, + LCD_INSTR_DAT, 0x04, + LCD_INSTR_DAT, 0x13, + LCD_INSTR_DAT, 0x07, + LCD_INSTR_DAT, 0x40, + LCD_INSTR_DAT, 0x39, + LCD_INSTR_DAT, 0x4f, + LCD_INSTR_DAT, 0x06, + LCD_INSTR_DAT, 0x0d, + LCD_INSTR_DAT, 0x0a, + LCD_INSTR_DAT, 0x1f, + LCD_INSTR_DAT, 0x22, + LCD_INSTR_DAT, 0x00, + /* Negative gamma correction? */ + LCD_INSTR_CMD, 0xe1, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x21, + LCD_INSTR_DAT, 0x24, + LCD_INSTR_DAT, 0x03, + LCD_INSTR_DAT, 0x0f, + LCD_INSTR_DAT, 0x05, + LCD_INSTR_DAT, 0x38, + LCD_INSTR_DAT, 0x32, + LCD_INSTR_DAT, 0x49, + LCD_INSTR_DAT, 0x00, + LCD_INSTR_DAT, 0x09, + LCD_INSTR_DAT, 0x08, + LCD_INSTR_DAT, 0x32, + LCD_INSTR_DAT, 0x35, + LCD_INSTR_DAT, 0x0f, + /* Exit Sleep */ + LCD_INSTR_CMD, 0x11, + LCD_INSTR_UDELAY, 120000, + /* Display On */ + LCD_INSTR_CMD, 0x29, + LCD_INSTR_UDELAY, 20000, +}; + +/* sleep and wake copied directly from m3k */ +static const uint32_t erosqnative_lcd_cmd_sleep[] = { + /* Display OFF */ + LCD_INSTR_CMD, 0x28, + /* Sleep IN */ + LCD_INSTR_CMD, 0x10, + LCD_INSTR_UDELAY, 5000, + LCD_INSTR_END, +}; + +static const uint32_t erosqnative_lcd_cmd_wake[] = { + /* Sleep OUT */ + LCD_INSTR_CMD, 0x11, + LCD_INSTR_UDELAY, 5000, + /* Display ON */ + LCD_INSTR_CMD, 0x29, + LCD_INSTR_END, +}; + +/* As far as I can tell, this is a sequence of commands sent before each + * DMA set. Original in OF was: + * {0x2c, 0x2c, 0x2c, 0x2c} + * But this set from the m3k seems to work the same, and makes more sense + * to me: + * {0x00, 0x00, 0x00, 0x2c} + * This command is more than likely going to be the same + * for any old mipi lcd on the market, maybe. I really don't think we need + * to send "write_memory_start four times in a row. */ +static const uint8_t __attribute__((aligned(64))) + erosqnative_lcd_dma_wr_cmd[] = {0x2c, 0x2c, 0x2c, 0x2c}; + +const struct lcd_tgt_config lcd_tgt_config = { + .bus_width = 8, + .cmd_width = 8, + .use_6800_mode = 0, + .use_serial = 0, + .clk_polarity = 0, + .dc_polarity = 0, + .wr_polarity = 1, + .te_enable = 0, /* OF had TE enabled (1) */ + .te_polarity = 1, + .te_narrow = 0, + .dma_wr_cmd_buf = &erosqnative_lcd_dma_wr_cmd, + .dma_wr_cmd_size = sizeof(erosqnative_lcd_dma_wr_cmd), +}; + +void lcd_tgt_enable(bool enable) +{ + if(enable) { + /* power up the panel */ + gpio_set_level(GPIO_LCD_PWR, 1); + mdelay(20); + gpio_set_level(GPIO_LCD_RESET, 1); + mdelay(12); + + /* set the clock */ + lcd_set_clock(X1000_CLK_SCLK_A, 20000000); + + /* toggle chip select low (active) */ + gpio_set_level(GPIO_LCD_RD, 1); + gpio_set_level(GPIO_LCD_CE, 1); + mdelay(5); + gpio_set_level(GPIO_LCD_CE, 0); + + lcd_exec_commands(&erosqnative_lcd_cmd_enable[0]); + } else { + /* doesn't flash white if we don't do anything... */ +#if 0 + lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]); + + mdelay(115); // copied from m3k + + gpio_set_level(GPIO_LCD_PWR, 0); + gpio_set_level(GPIO_LCD_RESET, 0); +#endif + } +} + +void lcd_tgt_sleep(bool sleep) +{ + if(sleep) + lcd_exec_commands(&erosqnative_lcd_cmd_sleep[0]); + else + lcd_exec_commands(&erosqnative_lcd_cmd_wake[0]); +} diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c new file mode 100644 index 0000000000..c466db66b1 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/power-erosqnative.c @@ -0,0 +1,110 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald, Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +// whole file copied from m3k + +#include "power.h" +#include "adc.h" +#include "system.h" +#include "kernel.h" +#ifdef HAVE_USB_CHARGING_ENABLE +# include "usb_core.h" +#endif +#include "axp-pmu.h" +#include "i2c-x1000.h" + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + 3470 +}; + +/* the OF shuts down at this voltage */ +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + 3400 +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + { 3400, 3639, 3697, 3723, 3757, 3786, 3836, 3906, 3980, 4050, 4159 } +}; + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = +{ + 3485, 3780, 3836, 3857, 3890, 3930, 3986, 4062, 4158, 4185, 4196 +}; + +void power_init(void) +{ + /* Initialize driver */ + i2c_x1000_set_freq(2, I2C_FREQ_400K); + axp_init(); + + /* Set lowest sample rate */ + axp_adc_set_rate(AXP_ADC_RATE_25HZ); + + /* Ensure battery voltage ADC is enabled */ + int bits = axp_adc_get_enabled(); + bits |= (1 << ADC_BATTERY_VOLTAGE); + axp_adc_set_enabled(bits); + + /* Turn on all power outputs */ + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_PWROUTPUTCTRL2, 0, 0x5f, NULL); + i2c_reg_modify1(AXP_PMU_BUS, AXP_PMU_ADDR, + AXP_REG_DCDCWORKINGMODE, 0, 0xc0, NULL); + + /* Set the default charging current. This is the same as the + * OF's setting, although it's not strictly within the USB spec. */ + axp_set_charge_current(780); + + /* Short delay to give power outputs time to stabilize */ + mdelay(5); +} + +#ifdef HAVE_USB_CHARGING_ENABLE +void usb_charging_maxcurrent_change(int maxcurrent) +{ + axp_set_charge_current(maxcurrent); +} +#endif + +void adc_init(void) +{ +} + +void power_off(void) +{ + axp_power_off(); + while(1); +} + +bool charging_state(void) +{ + return axp_battery_status() == AXP_BATT_CHARGING; +} + +int _battery_voltage(void) +{ + return axp_adc_read(ADC_BATTERY_VOLTAGE); +} diff --git a/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c b/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c new file mode 100644 index 0000000000..9d7a1d118a --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/erosqnative/spl-erosqnative.c @@ -0,0 +1,63 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "system.h" +#include "clk-x1000.h" +#include "spl-x1000.h" +#include "gpio-x1000.h" + +/* TODO: get dual-boot working */ + +const struct spl_boot_option spl_boot_options[] = { + [BOOT_OPTION_ROCKBOX] = { + .storage_addr = 0x6800, + .storage_size = 102 * 1024, + .load_addr = X1000_DRAM_BASE, + .exec_addr = X1000_DRAM_BASE, + .flags = BOOTFLAG_UCLPACK, + }, +}; + +int spl_get_boot_option(void) +{ + return BOOT_OPTION_ROCKBOX; +} + +void spl_error(void) +{ + const uint32_t pin = (1 << 25); + + /* Turn on backlight */ + jz_clr(GPIO_INT(GPIO_C), pin); + jz_set(GPIO_MSK(GPIO_C), pin); + jz_clr(GPIO_PAT1(GPIO_C), pin); + jz_set(GPIO_PAT0(GPIO_C), pin); + + while(1) { + /* Turn it off */ + mdelay(100); + jz_set(GPIO_PAT0(GPIO_C), pin); + + /* Turn it on */ + mdelay(100); + jz_clr(GPIO_PAT0(GPIO_C), pin); + } +} diff --git a/firmware/target/mips/ingenic_x1000/lcd-x1000.c b/firmware/target/mips/ingenic_x1000/lcd-x1000.c index 193ff082e0..b66359a598 100644 --- a/firmware/target/mips/ingenic_x1000/lcd-x1000.c +++ b/firmware/target/mips/ingenic_x1000/lcd-x1000.c @@ -457,6 +457,10 @@ void lcd_update(void) jz_writef(LCD_MCTRL, DMA_START(1), DMA_MODE(1)); } +/* We can do partial updates even though the DMA doesn't seem to handle it well, + * due to the fact that this is actually putting it into a buffer, and then + * it gets transferred via DMA to a secondary buffer, which gets transferred in + * its entirety to the LCD through a different DMA process. */ void lcd_update_rect(int x, int y, int width, int height) { /* Clamp the coordinates */ diff --git a/firmware/target/mips/ingenic_x1000/msc-x1000.c b/firmware/target/mips/ingenic_x1000/msc-x1000.c index d0359a53e2..dbc1ebbe89 100644 --- a/firmware/target/mips/ingenic_x1000/msc-x1000.c +++ b/firmware/target/mips/ingenic_x1000/msc-x1000.c @@ -63,6 +63,16 @@ static const msc_config msc_configs[] = { .cd_active_level = 0, }, /* NOTE: SDIO wifi card is on msc1 */ +#elif defined(EROS_QN) +#define MSC_CLOCK_SOURCE X1000_CLK_SCLK_A + { + .msc_nr = 0, + .msc_type = MSC_TYPE_SD, + .bus_width = 4, + .label = "microSD", + .cd_gpio = GPIO_MSC0_CD, + .cd_active_level = 0, + }, #else # error "Please add X1000 MSC config" #endif diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index de6eb2fb67..a818ba10aa 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c @@ -58,7 +58,7 @@ #define FREG_STATUS_ECC_UNCOR_ERR (2 << 4) const nand_chip supported_nand_chips[] = { -#if defined(FIIO_M3K) || defined(SHANLING_Q1) +#if defined(FIIO_M3K) || defined(SHANLING_Q1) || defined(EROS_QN) { /* ATO25D1GA */ .mf_id = 0x9b, diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c index 72dc53b2b7..afaf5a7dd6 100644 --- a/firmware/target/mips/ingenic_x1000/spl-x1000.c +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c @@ -38,6 +38,10 @@ # define SPL_DDR_MEMORYSIZE 64 # define SPL_DDR_AUTOSR_EN 1 # define SPL_DDR_NEED_BYPASS 1 +#elif defined(EROS_QN) +# define SPL_DDR_MEMORYSIZE 32 +# define SPL_DDR_AUTOSR_EN 1 +# define SPL_DDR_NEED_BYPASS 1 #else # error "please define DRAM settings" #endif diff --git a/tools/configure b/tools/configure index f32c5d514d..eb3a971366 100755 --- a/tools/configure +++ b/tools/configure @@ -1598,7 +1598,7 @@ cat <