From 4c60bc9e681865fcfc149775a1ed7ccd2613d5bf Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sun, 23 May 2021 17:30:58 +0100 Subject: New port: Shanling Q1 native - Audio playback works - Touchscreen and buttons work - Bootloader works and is capable of dual boot - Plugins are working - Cabbiev2 theme has been ported - Stable for general usage Thanks to Marc Aarts for porting Cabbiev2 and plugin bitmaps. There's a few minor known issues: - Bootloader must be installed manually using 'usbboot' as there is no support in jztool yet. - Keymaps may be lacking, need further testing and feedback. - Some plugins may not be fully adapted to the screen size and could benefit from further tweaking. - LCD shows abnormal effects under some circumstances: for example, after viewing a mostly black screen an afterimage appears briefly when going back to a brightly-lit screen. Sudden power-off without proper shutdown of the backlight causes a "dissolving" effect. - CW2015 battery reporting driver is buggy, and disabled for now. Battery reporting is currently voltage-based using the AXP192. Change-Id: I635e83f02a880192c5a82cb0861ad3a61c137c3a --- .../ingenic_x1000/shanlingq1/button-shanlingq1.c | 195 +++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c (limited to 'firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c') diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c new file mode 100644 index 0000000000..27c49a7bd7 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c @@ -0,0 +1,195 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * Copyright (C) 2021 Dana Conrad + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "button.h" +#include "touchscreen.h" +#include "ft6x06.h" +#include "axp-pmu.h" +#include "kernel.h" +#include "backlight.h" +#include "powermgmt.h" +#include "gpio-x1000.h" +#include "irq-x1000.h" +#include "i2c-x1000.h" +#include + +/* Volume wheel rotation */ +static volatile int wheel_pos = 0; + +/* Value of headphone detect register */ +static uint8_t hp_detect_reg = 0x00; + +/* Interval to poll the register */ +#define HPD_POLL_TIME (HZ/2) + +static int hp_detect_tmo_cb(struct timeout* tmo) +{ + i2c_descriptor* d = (i2c_descriptor*)tmo->data; + i2c_async_queue(AXP_PMU_BUS, TIMEOUT_NOBLOCK, I2C_Q_ADD, 0, d); + return HPD_POLL_TIME; +} + +static void hp_detect_init(void) +{ + /* TODO: replace this copy paste cruft with an API in axp-pmu */ + static struct timeout tmo; + static const uint8_t gpio_reg = AXP192_REG_GPIOSTATE1; + static i2c_descriptor desc = { + .slave_addr = AXP_PMU_ADDR, + .bus_cond = I2C_START | I2C_STOP, + .tran_mode = I2C_READ, + .buffer[0] = (void*)&gpio_reg, + .count[0] = 1, + .buffer[1] = &hp_detect_reg, + .count[1] = 1, + .callback = NULL, + .arg = 0, + .next = NULL, + }; + + /* Headphone detect is wired to AXP192 GPIO: set it to input state */ + i2c_reg_write1(AXP_PMU_BUS, AXP_PMU_ADDR, AXP192_REG_GPIO1FUNCTION, 0x01); + + /* Get an initial reading before startup */ + int r = i2c_reg_read1(AXP_PMU_BUS, AXP_PMU_ADDR, gpio_reg); + if(r >= 0) + hp_detect_reg = r; + + /* Poll the register every second */ + timeout_register(&tmo, &hp_detect_tmo_cb, HPD_POLL_TIME, (intptr_t)&desc); +} + +void button_init_device(void) +{ + /* Setup interrupts for the volume wheel */ + gpio_set_function(GPIO_WHEEL1, GPIOF_IRQ_EDGE(0)); + gpio_set_function(GPIO_WHEEL2, GPIOF_IRQ_EDGE(0)); + gpio_flip_edge_irq(GPIO_WHEEL1); + gpio_flip_edge_irq(GPIO_WHEEL2); + gpio_enable_irq(GPIO_WHEEL1); + gpio_enable_irq(GPIO_WHEEL2); + + /* Init touchscreen driver */ + i2c_x1000_set_freq(FT6x06_BUS, I2C_FREQ_400K); + ft6x06_init(); + + /* Reset touch controller */ + gpio_set_level(GPIO_FT6x06_POWER, 1); + gpio_set_level(GPIO_FT6x06_RESET, 0); + mdelay(5); + gpio_set_level(GPIO_FT6x06_RESET, 1); + + /* Enable ft6x06 interrupt */ + system_set_irq_handler(GPIO_TO_IRQ(GPIO_FT6x06_INTERRUPT), ft6x06_irq_handler); + gpio_set_function(GPIO_FT6x06_INTERRUPT, GPIOF_IRQ_EDGE(0)); + gpio_enable_irq(GPIO_FT6x06_INTERRUPT); + + /* Headphone detection */ + hp_detect_init(); +} + +int button_read_device(int* data) +{ + int r = 0; + + /* Read GPIO buttons, these are all active low */ + uint32_t b = REG_GPIO_PIN(GPIO_B); + if((b & (1 << 21)) == 0) r |= BUTTON_PREV; + if((b & (1 << 22)) == 0) r |= BUTTON_NEXT; + if((b & (1 << 28)) == 0) r |= BUTTON_PLAY; + if((b & (1 << 31)) == 0) r |= BUTTON_POWER; + + /* Check the wheel */ + int wheel_btn = 0; + int whpos = wheel_pos; + if(whpos > 3) + wheel_btn = BUTTON_VOL_DOWN; + else if(whpos < -3) + wheel_btn = BUTTON_VOL_UP; + + if(wheel_btn) { + wheel_pos = 0; + + /* Post the event (rapid motion is more reliable this way) */ + queue_post(&button_queue, wheel_btn, 0); + queue_post(&button_queue, wheel_btn|BUTTON_REL, 0); + + /* Poke the backlight */ + backlight_on(); + reset_poweroff_timer(); + } + + /* Handle touchscreen + * + * TODO: Support 2-point multitouch (useful for 3x3 grid mode) + * TODO: Support simple gestures by converting them to fake buttons + */ + int t = touchscreen_to_pixels(ft6x06_state.pos_x, ft6x06_state.pos_y, data); + if(ft6x06_state.event == FT6x06_EVT_PRESS || + ft6x06_state.event == FT6x06_EVT_CONTACT) { + /* Only set the button bit if the screen is being touched. */ + r |= t; + } + + return r; +} + +void touchscreen_enable_device(bool en) +{ + ft6x06_enable(en); + /* TODO: check if it's worth shutting off the power pin */ +} + +bool headphones_inserted(void) +{ + /* TODO: Also check if the headset button is detectable via an ADC. + * The AXP driver should probably get proper interrupt handling, + * that would be useful for more things than just GPIO polling. */ + return hp_detect_reg & 0x20 ? true : false; +} + +static void handle_wheel_irq(void) +{ + /* Wheel stuff adapted from button-erosqnative.c */ + static const int delta[16] = { 0, -1, 1, 0, + 1, 0, 0, -1, + -1, 0, 0, 1, + 0, 1, -1, 0 }; + static uint32_t state = 0; + state <<= 2; + state |= (REG_GPIO_PIN(GPIO_D) >> 2) & 3; + state &= 0xf; + + wheel_pos += delta[state]; +} + +void GPIOD02(void) +{ + handle_wheel_irq(); + gpio_flip_edge_irq(GPIO_WHEEL1); +} + +void GPIOD03(void) +{ + handle_wheel_irq(); + gpio_flip_edge_irq(GPIO_WHEEL2); +} -- cgit v1.2.3