summaryrefslogtreecommitdiff
path: root/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c')
-rw-r--r--firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c720
1 files changed, 496 insertions, 224 deletions
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
index ea4aa083e5..2c3358e7dc 100644
--- a/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
+++ b/firmware/target/arm/imx233/sansa-fuzeplus/button-fuzeplus.c
@@ -22,7 +22,7 @@
#include "system.h"
#include "system-target.h"
#include "pinctrl-imx233.h"
-#include "generic_i2c.h"
+#include "i2c-imx233.h"
#include "synaptics-rmi.h"
#include "lcd.h"
#include "string.h"
@@ -30,6 +30,7 @@
#include "button-imx233.h"
#include "touchpad.h"
#include "stdio.h"
+#include "font.h"
struct imx233_button_map_t imx233_button_map[] =
{
@@ -39,7 +40,428 @@ struct imx233_button_map_t imx233_button_map[] =
IMX233_BUTTON_(END, END(), "")
};
+/**
+ * RMI API
+ */
+
+#define RMI_I2C_ADDR 0x40
+
+static unsigned char dev_ctl_reg; /* cached value of control register */
+
+/* NOTE:
+ * RMI over i2c supports some special aliases on page 0x2 but this driver don't
+ * use them */
+
+struct rmi_xfer_t;
+typedef void (*rmi_xfer_cb_t)(struct rmi_xfer_t *xfer);
+
+/* Represent a typical RMI transaction: a first transfer to select the page
+ * and a second transfer to read/write registers. The API takes care of annoying
+ * details and will simply call the callback at the end of the transfer. */
+struct rmi_xfer_t
+{
+ struct imx233_i2c_xfer_t xfer_page; /* first transfer: page select */
+ struct imx233_i2c_xfer_t xfer_rw; /* second transfer: read/write */
+ uint8_t sel_page[2]; /* write command to select page */
+ uint8_t sel_reg; /* write command to select register */
+ volatile enum imx233_i2c_error_t status; /* transfer status */
+ rmi_xfer_cb_t callback; /* callback */
+};
+
+/* Synchronous transfer: add a semaphore to block */
+struct rmi_xfer_sync_t
+{
+ struct rmi_xfer_t xfer;
+ struct semaphore sema; /* semaphore for completion */
+};
+
+/* callback for first transfer: record error if any */
+static void rmi_i2c_first_callback(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
+{
+ struct rmi_xfer_t *rxfer = container_of(xfer, struct rmi_xfer_t, xfer_page);
+ /* record status */
+ rxfer->status = status;
+}
+
+/* callback for first transfer: handle error and callback */
+static void rmi_i2c_second_callback(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
+{
+ struct rmi_xfer_t *rxfer = container_of(xfer, struct rmi_xfer_t, xfer_rw);
+ /* record status, only if not skipping (ie the error was in first transfer) */
+ if(status != I2C_SKIP)
+ rxfer->status = status;
+ /* callback */
+ if(rxfer->callback)
+ rxfer->callback(rxfer);
+}
+
+/* build a rmi transaction to read/write registers; do NOT cross page boundary ! */
+static void rmi_build_xfer(struct rmi_xfer_t *xfer, bool read, int address,
+ int byte_count, unsigned char *buffer, rmi_xfer_cb_t callback)
+{
+ /* first transfer: change page */
+ xfer->xfer_page.next = &xfer->xfer_rw;
+ xfer->xfer_page.fast_mode = true;
+ xfer->xfer_page.dev_addr = RMI_I2C_ADDR;
+ xfer->xfer_page.mode = I2C_WRITE;
+ xfer->xfer_page.count[0] = 2;
+ xfer->xfer_page.data[0] = &xfer->sel_page;
+ xfer->xfer_page.count[1] = 0;
+ xfer->xfer_page.tmo_ms = 1000;
+ xfer->xfer_page.callback = &rmi_i2c_first_callback;
+ /* second transfer: read/write */
+ xfer->xfer_rw.next = NULL;
+ xfer->xfer_rw.fast_mode = true;
+ xfer->xfer_rw.dev_addr = RMI_I2C_ADDR;
+ xfer->xfer_rw.mode = read ? I2C_READ : I2C_WRITE;
+ xfer->xfer_rw.count[0] = 1;
+ xfer->xfer_rw.data[0] = &xfer->sel_reg;
+ xfer->xfer_rw.count[1] = byte_count;
+ xfer->xfer_rw.data[1] = buffer;
+ xfer->xfer_rw.tmo_ms = 1000;
+ xfer->xfer_rw.callback = &rmi_i2c_second_callback;
+ /* general things */
+ xfer->callback = callback;
+ xfer->sel_page[0] = RMI_PAGE_SELECT;
+ xfer->sel_page[1] = address >> 8;
+ xfer->sel_reg = address & 0xff;
+}
+
+/** IMPORTANT NOTE
+ *
+ * All transfers are built using rmi_build_xfer which constructs a transaction
+ * consisting in a page select and register read/writes. Since transactions are
+ * executed "atomically" and are queued, it is safe to call transfers functions
+ * concurrently. However only asynchronous transfers can be used in IRQ context.
+ * In all cases, make sure the the rmi_xfer_t structure lives at least until the
+ * completion of the transfer (callback).
+ */
+
+/* queue transfer to change sleep mode, return true if transfer was queued
+ * and false if ignored because requested mode is already the current one.
+ * call must provide a transfer structure that must exist until completion */
+static bool rmi_set_sleep_mode_async(struct rmi_xfer_t *xfer, uint8_t *buf,
+ unsigned char sleep_mode, rmi_xfer_cb_t callback)
+{
+ /* avoid any race with concurrent changes to the mode */
+ unsigned long cpsr = disable_irq_save();
+ /* valid value different from the actual one */
+ if((dev_ctl_reg & RMI_SLEEP_MODE_BM) != sleep_mode)
+ {
+ /* change cached version */
+ dev_ctl_reg &= ~RMI_SLEEP_MODE_BM;
+ dev_ctl_reg |= sleep_mode;
+ *buf = dev_ctl_reg;
+ restore_irq(cpsr);
+ /* build transfer and kick */
+ rmi_build_xfer(xfer, false, RMI_DEVICE_CONTROL, 1, buf, callback);
+ imx233_i2c_transfer(&xfer->xfer_page);
+ return true;
+ }
+ else
+ {
+ restore_irq(cpsr);
+ return false;
+ }
+}
+
+/* synchronous callback: release semaphore */
+static void rmi_i2c_sync_callback(struct rmi_xfer_t *xfer)
+{
+ struct rmi_xfer_sync_t *sxfer = (void *)xfer;
+ semaphore_release(&sxfer->sema);
+}
+
+/* synchronous read/write */
+static void rmi_rw(bool read, int address, int byte_count, unsigned char *buffer)
+{
+ struct rmi_xfer_sync_t xfer;
+ rmi_build_xfer(&xfer.xfer, read, address, byte_count, buffer, rmi_i2c_sync_callback);
+ semaphore_init(&xfer.sema, 1, 0);
+ /* kick and wait */
+ imx233_i2c_transfer(&xfer.xfer.xfer_page);
+ semaphore_wait(&xfer.sema, TIMEOUT_BLOCK);
+ if(xfer.xfer.status != I2C_SUCCESS)
+ panicf("rmi: i2c err %d", xfer.xfer.status);
+}
+
+/* read registers synchronously */
+static void rmi_read(int address, int byte_count, unsigned char *buffer)
+{
+ rmi_rw(true, address, byte_count, buffer);
+}
+
+/* read single register synchronously */
+static int rmi_read_single(int address)
+{
+ unsigned char c;
+ rmi_rw(true, address, 1, &c);
+ return c;
+}
+
+/* write single register synchronously */
+static void rmi_write_single(int address, unsigned char byte)
+{
+ return rmi_rw(false, address, 1, &byte);
+}
+
+/* synchronously change sleep mode, this is a nop if current mode is the same as requested */
+static void rmi_set_sleep_mode(unsigned char sleep_mode)
+{
+ struct rmi_xfer_sync_t xfer;
+ uint8_t buf;
+ semaphore_init(&xfer.sema, 1, 0);
+ /* kick asynchronous transfer and only wait if mode was actually changed */
+ if(rmi_set_sleep_mode_async(&xfer.xfer, &buf, sleep_mode, &rmi_i2c_sync_callback))
+ {
+ semaphore_wait(&xfer.sema, TIMEOUT_BLOCK);
+ if(xfer.xfer.status != I2C_SUCCESS)
+ panicf("rmi: i2c err %d", xfer.xfer.status);
+ }
+}
+
+static void rmi_init(void)
+{
+ /* cache control register */
+ dev_ctl_reg = rmi_read_single(RMI_DEVICE_CONTROL);
+}
+
+
+/**
+ * Touchpad API
+ */
+
#ifndef BOOTLOADER
+/* we emulate a 3x3 grid, this gives the button mapping */
+int button_mapping[3][3] =
+{
+ {BUTTON_BOTTOMLEFT, BUTTON_LEFT, BUTTON_BACK},
+ {BUTTON_DOWN, BUTTON_SELECT, BUTTON_UP},
+ {BUTTON_BOTTOMRIGHT, BUTTON_RIGHT, BUTTON_PLAYPAUSE},
+};
+
+/* timeout before lowering touchpad power from lack of activity */
+#define ACTIVITY_TMO (5 * HZ)
+#define TOUCHPAD_WIDTH 3010
+#define TOUCHPAD_HEIGHT 1975
+#define DEADZONE_MULTIPLIER 2 /* deadzone multiplier */
+
+/* power level when touchpad is active: experiments show that "low power" reduce
+ * power consumption and hardly makes a difference in quality. */
+#define ACTIVE_POWER_LEVEL RMI_SLEEP_MODE_LOW_POWER
+
+static int touchpad_btns = 0; /* button bitmap for the touchpad */
+static unsigned last_activity = 0; /* tick of the last touchpad activity */
+static bool t_enable = true; /* is touchpad enabled? */
+static int deadzone; /* deadzone size */
+static struct timeout activity_tmo; /* activity timeout */
+
+/* Ignore deadzone function. If outside of the pad, project to border. */
+static int find_button_no_deadzone(int x, int y)
+{
+ /* compute grid coordinate */
+ int gx = MAX(MIN(x * 3 / TOUCHPAD_WIDTH, 2), 0);
+ int gy = MAX(MIN(y * 3 / TOUCHPAD_HEIGHT, 2), 0);
+
+ return button_mapping[gx][gy];
+}
+
+static int find_button(int x, int y)
+{
+ /* find button ignoring deadzones */
+ int btn = find_button_no_deadzone(x, y);
+ /* To check if we are in a deadzone, we try to shift the coordinates
+ * and see if we get the same button. Not that we do not want to apply
+ * the deadzone in the borders ! The code works even in the borders because
+ * the find_button_no_deadzone() project out-of-bound coordinates to the
+ * borders */
+ if(find_button_no_deadzone(x + deadzone, y) != btn ||
+ find_button_no_deadzone(x - deadzone, y) != btn ||
+ find_button_no_deadzone(x, y + deadzone) != btn ||
+ find_button_no_deadzone(x, y - deadzone) != btn)
+ return 0;
+ return btn;
+}
+
+void touchpad_set_deadzone(int touchpad_deadzone)
+{
+ deadzone = touchpad_deadzone * DEADZONE_MULTIPLIER;
+}
+
+static int touchpad_read_device(void)
+{
+ return touchpad_btns;
+}
+
+/* i2c transfer only used for irq processing
+ * NOTE we use two sets of transfers because we setup one in the callback of the
+ * other, using one would be unsafe */
+static struct rmi_xfer_t rmi_irq_xfer[2];
+static uint8_t rmi_irq_buf; /* buffer to hold irq status register and sleep mode */
+static union
+{
+ unsigned char data[10];
+ struct
+ {
+ struct rmi_2d_absolute_data_t absolute;
+ struct rmi_2d_relative_data_t relative;
+ struct rmi_2d_gesture_data_t gesture;
+ }s;
+}rmi_irq_data; /* buffer to hold touchpad data */
+
+static void rmi_attn_cb(int bank, int pin, intptr_t user);
+
+/* callback for i2c transfer to change power level after irq */
+static void rmi_power_irq_cb(struct rmi_xfer_t *xfer)
+{
+ /* we do not recover from error for now */
+ if(xfer->status != I2C_SUCCESS)
+ panicf("rmi: clear i2c err %d", xfer->status);
+ /* now that interrupt is cleared, we can renable attention irq */
+ imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
+}
+
+/* callback for i2c transfer to read/clear interrupt status register */
+static void rmi_clear_irq_cb(struct rmi_xfer_t *xfer)
+{
+ /* we do not recover from error for now */
+ if(xfer->status != I2C_SUCCESS)
+ panicf("rmi: clear i2c err %d", xfer->status);
+ /* at this point, we might have processed an event and the touchpad still be
+ * in very low power mode because of some previous inactivity; if it's the case,
+ * schedule another transfer to switch to a higher power mode before accepting the
+ * next event */
+ /* kick asynchronous transfer and only wait if mode was actually changed */
+ if(!rmi_set_sleep_mode_async(&rmi_irq_xfer[0], &rmi_irq_buf, ACTIVE_POWER_LEVEL,
+ &rmi_power_irq_cb))
+ /* now that interrupt is cleared, we can renable attention irq */
+ imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
+}
+
+/* callback for i2c transfer to read touchpad data registers */
+static void rmi_data_irq_cb(struct rmi_xfer_t *xfer)
+{
+ /* we do not recover from error for now */
+ if(xfer->status != I2C_SUCCESS)
+ panicf("rmi: data i2c err %d", xfer->status);
+ /* now that we have the data, setup another transfer to clear interrupt */
+ rmi_build_xfer(&rmi_irq_xfer[1], true, RMI_INTERRUPT_REQUEST, 1,
+ &rmi_irq_buf, &rmi_clear_irq_cb);
+ /* kick transfer */
+ imx233_i2c_transfer(&rmi_irq_xfer[1].xfer_page);
+ /* now process touchpad data */
+ int absolute_x = rmi_irq_data.s.absolute.x_msb << 8 | rmi_irq_data.s.absolute.x_lsb;
+ int absolute_y = rmi_irq_data.s.absolute.y_msb << 8 | rmi_irq_data.s.absolute.y_lsb;
+ int nr_fingers = rmi_irq_data.s.absolute.misc & 7;
+ if(nr_fingers == 1)
+ touchpad_btns = find_button(absolute_x, absolute_y);
+ else
+ touchpad_btns = 0;
+}
+
+/* touchpad attention line interrupt */
+static void rmi_attn_cb(int bank, int pin, intptr_t user)
+{
+ (void) bank;
+ (void) pin;
+ (void) user;
+ /* build transfer to read data registers */
+ rmi_build_xfer(&rmi_irq_xfer[0], true, RMI_DATA_REGISTER(0),
+ sizeof(rmi_irq_data.data), rmi_irq_data.data, &rmi_data_irq_cb);
+ /* kick transfer */
+ imx233_i2c_transfer(&rmi_irq_xfer[0].xfer_page);
+ /* update last activity */
+ last_activity = current_tick;
+}
+
+void touchpad_enable_device(bool en)
+{
+ t_enable = en;
+ rmi_set_sleep_mode(en ? ACTIVE_POWER_LEVEL : RMI_SLEEP_MODE_SENSOR_SLEEP);
+}
+
+void touchpad_set_sensitivity(int level)
+{
+ /* handle negative values as well ! */
+ rmi_write_single(RMI_2D_SENSITIVITY_ADJ, (unsigned char)(int8_t)level);
+}
+
+/* transfer used by the activity timeout to change power level */
+static struct rmi_xfer_t rmi_tmo_xfer;
+static uint8_t rmi_tmo_buf;
+
+/* activity timeout: lower power level after some inactivity */
+static int activity_monitor(struct timeout *tmo)
+{
+ (void) tmo;
+ if(TIME_AFTER(current_tick, last_activity + ACTIVITY_TMO))
+ {
+ /* don't change power mode if touchpad is disabled, it's already in sleep mode */
+ if(t_enable)
+ rmi_set_sleep_mode_async(&rmi_tmo_xfer, &rmi_tmo_buf,
+ RMI_SLEEP_MODE_VERY_LOW_POWER, NULL);
+ }
+
+ return HZ; /* next check in 1 second */
+}
+
+void touchpad_init(void)
+{
+ /* Synaptics TouchPad information:
+ * - product id: 1533
+ * - nr function: 1 (0x10 = 2D touchpad)
+ * 2D Touchpad information (function 0x10)
+ * - nr data sources: 3
+ * - standard layout
+ * - extra data registers: 7
+ * - nr sensors: 1
+ * 2D Touchpad Sensor #0 information:
+ * - has relative data: yes
+ * - has palm detect: yes
+ * - has multi finger: yes
+ * - has enhanced gesture: yes
+ * - has scroller: no
+ * - has 2D scrollers: no
+ * - Maximum X: 3009
+ * - Maxumum Y: 1974
+ * - Resolution: 82
+ */
+
+ imx233_pinctrl_acquire(0, 26, "touchpad power");
+ imx233_pinctrl_set_function(0, 26, PINCTRL_FUNCTION_GPIO);
+ imx233_pinctrl_enable_gpio(0, 26, false);
+ imx233_pinctrl_set_drive(0, 26, PINCTRL_DRIVE_8mA);
+
+ /* use a timer to monitor touchpad activity and manage power level */
+ last_activity = current_tick;
+ timeout_register(&activity_tmo, activity_monitor, HZ, 0);
+
+ rmi_init();
+
+ char product_id[RMI_PRODUCT_ID_LEN];
+ rmi_read(RMI_PRODUCT_ID, RMI_PRODUCT_ID_LEN, product_id);
+ /* The OF adjust the sensitivity based on product_id[1] compared to 2.
+ * Since it doesn't seem to work great, just hardcode the sensitivity to
+ * some reasonable value for now. */
+ rmi_write_single(RMI_2D_SENSITIVITY_ADJ, 13);
+
+ rmi_write_single(RMI_2D_GESTURE_SETTINGS,
+ RMI_2D_GESTURE_PRESS_TIME_300MS |
+ RMI_2D_GESTURE_FLICK_DIST_4MM << RMI_2D_GESTURE_FLICK_DIST_BP |
+ RMI_2D_GESTURE_FLICK_TIME_700MS << RMI_2D_GESTURE_FLICK_TIME_BP);
+
+ /* we don't know in which mode the touchpad start so use a sane default */
+ rmi_set_sleep_mode(ACTIVE_POWER_LEVEL);
+ /* enable interrupt */
+ imx233_pinctrl_acquire(0, 27, "touchpad int");
+ imx233_pinctrl_set_function(0, 27, PINCTRL_FUNCTION_GPIO);
+ imx233_pinctrl_enable_gpio(0, 27, false);
+ imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
+}
+
+/**
+ * Debug screen
+ */
bool button_debug_screen(void)
{
@@ -82,9 +504,27 @@ bool button_debug_screen(void)
gesture_vp.y = zone_y - 80;
gesture_vp.width = LCD_WIDTH / 2;
gesture_vp.height = 80;
-
+ /* remember tick of last gestures */
+ #define GESTURE_TMO HZ / 2
+ int tick_last_tap = current_tick - GESTURE_TMO;
+ int tick_last_doubletap = current_tick - GESTURE_TMO;
+ int tick_last_taphold = current_tick - GESTURE_TMO;
+ int tick_last_flick = current_tick - GESTURE_TMO;
+ int flick_x = 0, flick_y = 0;
+
+ /* BUG the data register are usually read by the IRQ already and it is
+ * important to not read them again, otherwise we could miss some events
+ * (most notable gestures). However, we only read registers when the
+ * touchpad is active so the data might be outdated if touchpad is
+ * inactive. We should implement a continuous reading mode for the debug
+ * screen. */
+
+ lcd_setfont(FONT_SYSFIXED);
while(1)
{
+ /* call button_get() to avoid an overflow in the button queue */
+ button_get(false);
+
unsigned char sleep_mode = rmi_read_single(RMI_DEVICE_CONTROL) & RMI_SLEEP_MODE_BM;
lcd_set_viewport(NULL);
lcd_clear_display();
@@ -92,10 +532,9 @@ bool button_debug_screen(void)
lcd_putsf(0, 0, "button bitmap: %x", btns);
lcd_putsf(0, 1, "RMI: id=%s info=%s", product_id, product_info_str);
lcd_putsf(0, 2, "xmax=%d ymax=%d res=%d", x_max, y_max, sensor_resol);
- lcd_putsf(0, 3, "attn=%d ctl=%x int=%x",
+ lcd_putsf(0, 3, "attn=%d ctl=%x",
imx233_pinctrl_get_gpio(0, 27) ? 0 : 1,
- rmi_read_single(RMI_DEVICE_CONTROL),
- rmi_read_single(RMI_INTERRUPT_REQUEST));
+ rmi_read_single(RMI_DEVICE_CONTROL));
lcd_putsf(0, 4, "sensi: %d min_dist: %d", (int)sensitivity.value, min_dist);
lcd_putsf(0, 5, "gesture: %x", gesture_settings);
@@ -109,21 +548,49 @@ bool button_debug_screen(void)
struct rmi_2d_gesture_data_t gesture;
}s;
}u;
+
+ /* Disable IRQs when reading to avoid reading incorrect data */
+ unsigned long cpsr = disable_irq_save();
+ memcpy(&u, &rmi_irq_data, sizeof(u));
+ restore_irq(cpsr);
+
int absolute_x = u.s.absolute.x_msb << 8 | u.s.absolute.x_lsb;
int absolute_y = u.s.absolute.y_msb << 8 | u.s.absolute.y_lsb;
int nr_fingers = u.s.absolute.misc & 7;
bool gesture = (u.s.absolute.misc & 8) == 8;
int palm_width = u.s.absolute.misc >> 4;
- rmi_read(RMI_DATA_REGISTER(0), 10, u.data);
lcd_putsf(0, 6, "abs: %d %d %d", absolute_x, absolute_y, (int)u.s.absolute.z);
lcd_putsf(0, 7, "rel: %d %d", (int)u.s.relative.x, (int)u.s.relative.y);
lcd_putsf(0, 8, "gesture: %x %x", u.s.gesture.misc, u.s.gesture.flick);
lcd_putsf(0, 9, "misc: w=%d g=%d f=%d", palm_width, gesture, nr_fingers);
- lcd_putsf(30, 7, "sleep_mode: %d", 1 - sleep_mode);
+ lcd_putsf(0, 10, "sleep_mode: %d", sleep_mode);
+ lcd_putsf(0, 11, "deadzone: %d", deadzone);
+ /* display virtual touchpad with deadzones */
lcd_set_viewport(&report_vp);
+ lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0xff, 0), LCD_BLACK);
+ for(int i = 0; i < 3; i++)
+ for(int j = 0; j < 3; j++)
+ {
+ int x = j * x_max / 3;
+ if(j != 0)
+ x += deadzone;
+ int x2 = (j + 1) * x_max / 3;
+ if(j != 2)
+ x2 -= deadzone;
+ int y = i * y_max / 3;
+ if(i != 0)
+ y += deadzone;
+ int y2 = (i + 1) * y_max / 3;
+ if(i != 2)
+ y2 -= deadzone;
+ x = DX2SX(x); x2 = DX2SX(x2); y = DY2SY(y); y2 = DY2SY(y2);
+ lcd_drawrect(x, y2, x2 - x + 1, y - y2 + 1);
+ }
lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0xff, 0, 0), LCD_BLACK);
lcd_drawrect(0, 0, zone_w, zone_h);
+ /* put a done at the reported position of the finger
+ * also display relative motion by a line as reported by the device */
if(nr_fingers == 1)
{
lcd_set_drawinfo(DRMODE_SOLID, LCD_RGBPACK(0, 0, 0xff), LCD_BLACK);
@@ -141,27 +608,37 @@ bool button_debug_screen(void)
{
case RMI_2D_GEST_MISC_NO_TAP: break;
case RMI_2D_GEST_MISC_SINGLE_TAP:
- lcd_putsf(0, 0, "TAP!");
+ tick_last_tap = current_tick;
break;
case RMI_2D_GEST_MISC_DOUBLE_TAP:
- lcd_putsf(0, 0, "DOUBLE TAP!");
+ tick_last_doubletap = current_tick;
break;
case RMI_2D_GEST_MISC_TAP_AND_HOLD:
- lcd_putsf(0, 0, "TAP & HOLD!");
+ tick_last_taphold = current_tick;
break;
default: break;
}
if(u.s.gesture.misc & RMI_2D_GEST_MISC_FLICK)
{
- lcd_putsf(0, 1, "FLICK!");
- int flick_x = u.s.gesture.flick & RMI_2D_GEST_FLICK_X_BM;
- int flick_y = (u.s.gesture.flick & RMI_2D_GEST_FLICK_Y_BM) >> RMI_2D_GEST_FLICK_Y_BP;
+ tick_last_flick = current_tick;
+ flick_x = u.s.gesture.flick & RMI_2D_GEST_FLICK_X_BM;
+ flick_y = (u.s.gesture.flick & RMI_2D_GEST_FLICK_Y_BM) >> RMI_2D_GEST_FLICK_Y_BP;
#define SIGN4EXT(a) \
if(a & 8) a = -((a ^ 0xf) + 1);
SIGN4EXT(flick_x);
SIGN4EXT(flick_y);
+ }
+ if(TIME_BEFORE(current_tick, tick_last_tap + GESTURE_TMO))
+ lcd_putsf(0, 0, "TAP!");
+ if(TIME_BEFORE(current_tick, tick_last_doubletap + GESTURE_TMO))
+ lcd_putsf(0, 1, "DOUBLE TAP!");
+ if(TIME_BEFORE(current_tick, tick_last_taphold + GESTURE_TMO))
+ lcd_putsf(0, 2, "TAP & HOLD!");
+ if(TIME_BEFORE(current_tick, tick_last_flick + GESTURE_TMO))
+ {
+ lcd_putsf(0, 3, "FLICK!");
int center_x = (LCD_WIDTH * 2) / 3;
int center_y = 40;
lcd_drawline(center_x, center_y, center_x + flick_x * 5, center_y - flick_y * 5);
@@ -185,228 +662,23 @@ bool button_debug_screen(void)
volkeys_delay_counter = 0;
}
}
-
- yield();
}
+ lcd_set_viewport(NULL);
+ lcd_setfont(FONT_UI);
return true;
}
-/* we emulate a 3x3 grid, this gives the button mapping */
-int button_mapping[3][3] =
-{
- {BUTTON_BOTTOMLEFT, BUTTON_LEFT, BUTTON_BACK},
- {BUTTON_DOWN, BUTTON_SELECT, BUTTON_UP},
- {BUTTON_BOTTOMRIGHT, BUTTON_RIGHT, BUTTON_PLAYPAUSE},
-};
-
-#define RMI_INTERRUPT 1
-#define RMI_SET_SENSITIVITY 2
-#define RMI_SET_SLEEP_MODE 3
-/* timeout before lowering touchpad power from lack of activity */
-#define ACTIVITY_TMO (5 * HZ)
-#define TOUCHPAD_WIDTH 3010
-#define TOUCHPAD_HEIGHT 1975
-#define DEADZONE_MULTIPLIER 2 /* deadzone multiplier */
-
-static int touchpad_btns = 0;
-static long rmi_stack [DEFAULT_STACK_SIZE/sizeof(long)];
-static const char rmi_thread_name[] = "rmi";
-static struct event_queue rmi_queue;
-static unsigned last_activity = 0;
-static bool t_enable = true;
-static int deadzone;
-
-/* Ignore deadzone function. If outside of the pad, project to border. */
-static int find_button_no_deadzone(int x, int y)
-{
- /* compute grid coordinate */
- int gx = MAX(MIN(x * 3 / TOUCHPAD_WIDTH, 2), 0);
- int gy = MAX(MIN(y * 3 / TOUCHPAD_HEIGHT, 2), 0);
-
- return button_mapping[gx][gy];
-}
-
-static int find_button(int x, int y)
-{
- /* find button ignoring deadzones */
- int btn = find_button_no_deadzone(x, y);
- /* To check if we are in a deadzone, we try to shift the coordinates
- * and see if we get the same button. Not that we do not want to apply
- * the deadzone in the borders ! The code works even in the borders because
- * the find_button_no_deadzone() project out-of-bound coordinates to the
- * borders */
- if(find_button_no_deadzone(x + deadzone, y) != btn ||
- find_button_no_deadzone(x - deadzone, y) != btn ||
- find_button_no_deadzone(x, y + deadzone) != btn ||
- find_button_no_deadzone(x, y - deadzone) != btn)
- return 0;
- return btn;
-}
-
-void touchpad_set_deadzone(int touchpad_deadzone)
-{
- deadzone = touchpad_deadzone * DEADZONE_MULTIPLIER;
-}
-
-static int touchpad_read_device(void)
-{
- return touchpad_btns;
-}
-
-static void rmi_attn_cb(int bank, int pin, intptr_t user)
-{
- (void) bank;
- (void) pin;
- (void) user;
- /* the callback will not be fired until interrupt is enabled back so
- * the queue will not overflow or contain multiple RMI_INTERRUPT events */
- queue_post(&rmi_queue, RMI_INTERRUPT, 0);
-}
-
-static void do_interrupt(void)
-{
- /* rmi_set_sleep_mode() does not do anything if the value
- * it is given is already the one setted */
- rmi_set_sleep_mode(RMI_SLEEP_MODE_LOW_POWER);
- last_activity = current_tick;
- /* clear interrupt */
- rmi_read_single(RMI_INTERRUPT_REQUEST);
- /* read data */
- union
- {
- unsigned char data[10];
- struct
- {
- struct rmi_2d_absolute_data_t absolute;
- struct rmi_2d_relative_data_t relative;
- struct rmi_2d_gesture_data_t gesture;
- }s;
- }u;
- rmi_read(RMI_DATA_REGISTER(0), 10, u.data);
- int absolute_x = u.s.absolute.x_msb << 8 | u.s.absolute.x_lsb;
- int absolute_y = u.s.absolute.y_msb << 8 | u.s.absolute.y_lsb;
- int nr_fingers = u.s.absolute.misc & 7;
-
- if(nr_fingers == 1)
- touchpad_btns = find_button(absolute_x, absolute_y);
- else
- touchpad_btns = 0;
-
- /* enable interrupt */
- imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
-}
-
-void touchpad_enable_device(bool en)
-{
- t_enable = en;
- queue_post(&rmi_queue, RMI_SET_SLEEP_MODE, en ? RMI_SLEEP_MODE_LOW_POWER : RMI_SLEEP_MODE_SENSOR_SLEEP);
-}
-
-void touchpad_set_sensitivity(int level)
-{
- queue_post(&rmi_queue, RMI_SET_SENSITIVITY, level);
-}
-
-static void rmi_thread(void)
-{
- struct queue_event ev;
-
- while(1)
- {
- /* make sure to timeout often enough for the activity timeout to take place */
- queue_wait_w_tmo(&rmi_queue, &ev, HZ);
- /* handle usb connect and ignore all messages except rmi interrupts */
- switch(ev.id)
- {
- case SYS_USB_CONNECTED:
- usb_acknowledge(SYS_USB_CONNECTED_ACK);
- break;
- case RMI_SET_SENSITIVITY:
- /* handle negative values as well ! */
- rmi_write_single(RMI_2D_SENSITIVITY_ADJ, (unsigned char)(int8_t)ev.data);
- break;
- case RMI_SET_SLEEP_MODE:
- /* reset activity */
- last_activity = current_tick;
- rmi_set_sleep_mode(ev.data);
- break;
- case RMI_INTERRUPT:
- do_interrupt();
- break;
- default:
- /* activity timeout */
- if(TIME_AFTER(current_tick, last_activity + ACTIVITY_TMO))
- {
- /* don't change power mode if touchpad is disabled, it's already in sleep mode */
- if(t_enable)
- rmi_set_sleep_mode(RMI_SLEEP_MODE_VERY_LOW_POWER);
- }
- break;
- }
- }
-}
-
-static void touchpad_init(void)
-{
- /* Synaptics TouchPad information:
- * - product id: 1533
- * - nr function: 1 (0x10 = 2D touchpad)
- * 2D Touchpad information (function 0x10)
- * - nr data sources: 3
- * - standard layout
- * - extra data registers: 7
- * - nr sensors: 1
- * 2D Touchpad Sensor #0 information:
- * - has relative data: yes
- * - has palm detect: yes
- * - has multi finger: yes
- * - has enhanced gesture: yes
- * - has scroller: no
- * - has 2D scrollers: no
- * - Maximum X: 3009
- * - Maxumum Y: 1974
- * - Resolution: 82
- */
-
- imx233_pinctrl_acquire(0, 26, "touchpad power");
- imx233_pinctrl_set_function(0, 26, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_enable_gpio(0, 26, false);
- imx233_pinctrl_set_drive(0, 26, PINCTRL_DRIVE_8mA);
-
- rmi_init(0x40);
-
- char product_id[RMI_PRODUCT_ID_LEN];
- rmi_read(RMI_PRODUCT_ID, RMI_PRODUCT_ID_LEN, product_id);
- /* The OF adjust the sensitivity based on product_id[1] compared to 2.
- * Since it doesn't to work great, just hardcode the sensitivity to
- * some reasonable value for now. */
- rmi_write_single(RMI_2D_SENSITIVITY_ADJ, 13);
-
- rmi_write_single(RMI_2D_GESTURE_SETTINGS,
- RMI_2D_GESTURE_PRESS_TIME_300MS |
- RMI_2D_GESTURE_FLICK_DIST_4MM << RMI_2D_GESTURE_FLICK_DIST_BP |
- RMI_2D_GESTURE_FLICK_TIME_700MS << RMI_2D_GESTURE_FLICK_TIME_BP);
-
- queue_init(&rmi_queue, true);
- create_thread(rmi_thread, rmi_stack, sizeof(rmi_stack), 0,
- rmi_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
- /* low power mode seems to be enough for normal use */
- rmi_set_sleep_mode(RMI_SLEEP_MODE_LOW_POWER);
- /* enable interrupt */
- imx233_pinctrl_acquire(0, 27, "touchpad int");
- imx233_pinctrl_set_function(0, 27, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_enable_gpio(0, 27, false);
- imx233_pinctrl_setup_irq(0, 27, true, true, false, &rmi_attn_cb, 0);
-}
-
-#else
+#else /* BOOTLOADER */
int touchpad_read_device(void)
{
return 0;
}
#endif
+/**
+ * Button API
+ */
void button_init_device(void)
{
#ifndef BOOTLOADER