From 3ec66893e377b088c1284d2d23adb2aeea6d7965 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 27 Feb 2021 22:08:58 +0000 Subject: New port: FiiO M3K on bare metal Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe --- .../target/mips/ingenic_x1000/fiiom3k/adc-target.h | 0 .../mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c | 81 ++++ .../mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c | 88 ++++ .../mips/ingenic_x1000/fiiom3k/backlight-target.h | 37 ++ .../mips/ingenic_x1000/fiiom3k/button-fiiom3k.c | 503 +++++++++++++++++++++ .../mips/ingenic_x1000/fiiom3k/button-target.h | 54 +++ .../target/mips/ingenic_x1000/fiiom3k/i2c-target.h | 37 ++ .../mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c | 195 ++++++++ .../mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c | 192 ++++++++ .../mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c | 53 +++ .../mips/ingenic_x1000/fiiom3k/nand-target.h | 42 ++ .../mips/ingenic_x1000/fiiom3k/power-fiiom3k.c | 97 ++++ .../mips/ingenic_x1000/fiiom3k/powermgmt-target.h | 0 13 files changed, 1379 insertions(+) create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c create mode 100644 firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h (limited to 'firmware/target/mips/ingenic_x1000/fiiom3k') diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/adc-target.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c new file mode 100644 index 0000000000..2f43809523 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/audiohw-fiiom3k.c @@ -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; +} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c new file mode 100644 index 0000000000..f02fcaaee8 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-fiiom3k.c @@ -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); +} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h new file mode 100644 index 0000000000..791a013c32 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/backlight-target.h @@ -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 + +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__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c new file mode 100644 index 0000000000..db5ece10b0 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c @@ -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 +#include + +#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 diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h new file mode 100644 index 0000000000..f75a43242d --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-target.h @@ -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 + +#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__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h new file mode 100644 index 0000000000..a389d2af42 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/i2c-target.h @@ -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__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c new file mode 100644 index 0000000000..c794da4000 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c @@ -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"; + } +} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c new file mode 100644 index 0000000000..96f794d7df --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c @@ -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]); +} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c new file mode 100644 index 0000000000..7c8a306bae --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c @@ -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}, +}; diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h new file mode 100644 index 0000000000..26a8b840c9 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h @@ -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__ */ diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c new file mode 100644 index 0000000000..3eb3146d97 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c @@ -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); +} diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/powermgmt-target.h new file mode 100644 index 0000000000..e69de29bb2 -- cgit v1.2.3