rockbox/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
Aidan MacDonald 2d89143962 AXP PMU rewrite (again)
I noticed a few mistakes in the old driver code and it was in
need of an overhaul anyway... I decided to scale things back,
simplify the code and remove most of the debug menus, netting
a nice code size savings.

One new feature is an advanced debug menu which is accessible
by recompiling the code with AXP_EXTRA_DEBUG. It adds quite a
bit of code size and isn't useful other than for development
so it must be manually enabled by editing the source.

Change-Id: I30e17c1194c14823decd726a574ed14451d4cb2d
2021-12-28 15:20:27 +00:00

258 lines
7.6 KiB
C

/***************************************************************************
* __________ __ ___.
* 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 "axp192.h"
#include "kernel.h"
#include "backlight.h"
#include "powermgmt.h"
#include "gpio-x1000.h"
#include "irq-x1000.h"
#include "i2c-x1000.h"
#include <stdbool.h>
#ifndef BOOTLOADER
# include "lcd.h"
# include "font.h"
#endif
/* 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 = AXP_REG_GPIOLEVEL1;
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 */
axp_set_gpio_function(1, AXP_GPIO_INPUT);
/* Get an initial reading before startup */
int r = axp_read(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)
{
const struct ft6x06_point* point;
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();
}
if(touchscreen_get_mode() == TOUCHSCREEN_POINT) {
/* Pointing mode can't use multitouch since we can only pass
* along coordinates for one touch event at a time */
point = &ft6x06_state.points[0];
int t = touchscreen_to_pixels(point->pos_x, point->pos_y, data);
if(point->event == FT6x06_EVT_PRESS ||
point->event == FT6x06_EVT_CONTACT)
r |= t;
} else {
/* 3x3 mode can have simultaneous 'button' presses via multitouch */
for(int i = 0; i < ft6x06_state.nr_points; ++i) {
point = &ft6x06_state.points[i];
if(point->event == FT6x06_EVT_PRESS ||
point->event == FT6x06_EVT_CONTACT)
r |= touchscreen_to_pixels(point->pos_x, point->pos_y, NULL);
}
}
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);
}
#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_shanlingq1_touchscreen(void)
{
/* definition of box used to represent the touchpad */
const int pad_w = LCD_WIDTH;
const int pad_h = LCD_HEIGHT;
const int box_h = pad_h - SYSFONT_HEIGHT*5;
const int box_w = pad_w * box_h / pad_h;
const int box_x = (LCD_WIDTH - box_w) / 2;
const int box_y = SYSFONT_HEIGHT * 9 / 2;
bool draw_border = true;
do {
int line = 0;
lcd_clear_display();
lcd_putsf(0, line++, "nr_points: %d gesture: %d",
ft6x06_state.nr_points, ft6x06_state.gesture);
/* draw touchpad box borders */
if(draw_border)
lcd_drawrect(box_x, box_y, box_w, box_h);
for(int i = 0; i < ft6x06_state.nr_points; ++i) {
const struct ft6x06_point* point = &ft6x06_state.points[i];
lcd_putsf(0, line++, "pt%d id:%d pos: %d,%d wgt: %d area:%d",
i, point->touch_id, point->pos_x, point->pos_y,
point->weight, point->area);
/* draw crosshair */
int tx = box_x + point->pos_x * box_w / pad_w;
int ty = box_y + point->pos_y * box_h / pad_h;
lcd_hline(tx-2, tx+2, ty);
lcd_vline(tx, ty-2, ty+2);
}
lcd_update();
} while(getbtn() != BUTTON_POWER);
return false;
}
#endif