A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

Shanling Q1: enable multi-touch reporting

The FT6x06 driver used for the Shanling Q1's touchscreen
has been extended to report more than one touch point. It
can also return the gesture detected by the controller,
but this doesn't seem to report anything useful on the Q1.

Multi-touch is only useful in 3x3 grid mode since the Rockbox
button API cannot report more than one touch point.

The FiiO M3K uses the same driver so it's been updated to the
multi-touch API, but functionality is unchanged.

Change-Id: I4de42f44808d6eb902e3da212d8f936b7a5042c7

+139 -44
+39 -25
firmware/drivers/ft6x06.c
··· 24 24 #include "i2c-async.h" 25 25 #include <string.h> 26 26 27 + #define BYTES_PER_POINT 6 28 + 29 + #ifdef FT6x06_SWAP_AXES 30 + # define POS_X pos_y 31 + # define POS_Y pos_x 32 + #else 33 + # define POS_X pos_x 34 + # define POS_Y pos_y 35 + #endif 36 + 27 37 struct ft6x06_driver { 28 38 /* i2c bus data */ 29 39 int i2c_cookie; ··· 33 43 ft6x06_event_cb event_cb; 34 44 35 45 /* buffer for I2C transfers */ 36 - uint8_t raw_data[6]; 46 + uint8_t raw_data[1 + 2 + BYTES_PER_POINT * FT6x06_NUM_POINTS]; 37 47 }; 38 48 39 49 static struct ft6x06_driver ft_drv; 40 50 struct ft6x06_state ft6x06_state; 41 51 52 + static inline void ft6x06_convert_point(const uint8_t* raw, 53 + struct ft6x06_point* pt) 54 + { 55 + pt->event = (raw[0] >> 6) & 0x3; 56 + pt->touch_id = (raw[2] >> 4) & 0xf; 57 + pt->POS_X = ((raw[0] & 0xf) << 8) | raw[1]; 58 + pt->POS_Y = ((raw[2] & 0xf) << 8) | raw[3]; 59 + pt->weight = raw[4]; 60 + pt->area = (raw[5] >> 4) & 0xf; 61 + } 62 + 42 63 static void ft6x06_i2c_callback(int status, i2c_descriptor* desc) 43 64 { 44 65 (void)desc; 45 66 if(status != I2C_STATUS_OK) 46 67 return; 47 68 48 - int evt = ft_drv.raw_data[1] >> 6; 49 - int tx = ft_drv.raw_data[2] | ((ft_drv.raw_data[1] & 0xf) << 8); 50 - int ty = ft_drv.raw_data[4] | ((ft_drv.raw_data[3] & 0xf) << 8); 51 - 52 - ft6x06_state.event = evt; 53 - #ifdef FT6x06_SWAP_AXES 54 - ft6x06_state.pos_x = ty; 55 - ft6x06_state.pos_y = tx; 56 - #else 57 - ft6x06_state.pos_x = tx; 58 - ft6x06_state.pos_y = ty; 59 - #endif 69 + ft6x06_state.gesture = ft_drv.raw_data[1]; 70 + ft6x06_state.nr_points = ft_drv.raw_data[2] & 0xf; 71 + for(int i = 0; i < FT6x06_NUM_POINTS; ++i) { 72 + ft6x06_convert_point(&ft_drv.raw_data[3 + i * BYTES_PER_POINT], 73 + &ft6x06_state.points[i]); 74 + } 60 75 61 - ft_drv.event_cb(evt, ft6x06_state.pos_x, ft6x06_state.pos_y); 76 + ft_drv.event_cb(&ft6x06_state); 62 77 } 63 78 64 - static void ft6x06_dummy_event_cb(int evt, int tx, int ty) 79 + static void ft6x06_dummy_event_cb(struct ft6x06_state* state) 65 80 { 66 - (void)evt; 67 - (void)tx; 68 - (void)ty; 81 + (void)state; 69 82 } 70 83 71 84 void ft6x06_init(void) ··· 74 87 memset(&ft_drv, 0, sizeof(ft_drv)); 75 88 ft_drv.event_cb = ft6x06_dummy_event_cb; 76 89 77 - ft6x06_state.event = FT6x06_EVT_NONE; 78 - ft6x06_state.pos_x = 0; 79 - ft6x06_state.pos_y = 0; 90 + memset(&ft6x06_state, 0, sizeof(struct ft6x06_state)); 91 + ft6x06_state.gesture = -1; 92 + for(int i = 0; i < FT6x06_NUM_POINTS; ++i) 93 + ft6x06_state.points[i].event = FT6x06_EVT_NONE; 80 94 81 95 /* Reserve bus management cookie */ 82 96 ft_drv.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1); ··· 85 99 ft_drv.i2c_desc.slave_addr = FT6x06_ADDR; 86 100 ft_drv.i2c_desc.bus_cond = I2C_START | I2C_STOP; 87 101 ft_drv.i2c_desc.tran_mode = I2C_READ; 88 - ft_drv.i2c_desc.buffer[0] = &ft_drv.raw_data[5]; 102 + ft_drv.i2c_desc.buffer[0] = &ft_drv.raw_data[0]; 89 103 ft_drv.i2c_desc.count[0] = 1; 90 - ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[0]; 91 - ft_drv.i2c_desc.count[1] = 5; 104 + ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[1]; 105 + ft_drv.i2c_desc.count[1] = sizeof(ft_drv.raw_data) - 1; 92 106 ft_drv.i2c_desc.callback = ft6x06_i2c_callback; 93 107 ft_drv.i2c_desc.arg = 0; 94 108 ft_drv.i2c_desc.next = NULL; 95 109 96 110 /* Set I2C register address */ 97 - ft_drv.raw_data[5] = 0x02; 111 + ft_drv.raw_data[0] = 0x01; 98 112 } 99 113 100 114 void ft6x06_set_event_cb(ft6x06_event_cb cb)
+1
firmware/export/config/fiiom3k.h
··· 24 24 #define HAVE_I2C_ASYNC 25 25 #define HAVE_FT6x06 26 26 #define FT6x06_SWAP_AXES 27 + #define FT6x06_NUM_POINTS 1 27 28 28 29 /* Buffer for plugins and codecs. */ 29 30 #define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */
+1
firmware/export/config/shanlingq1.h
··· 59 59 #define HAVE_TOUCHSCREEN 60 60 #define HAVE_BUTTON_DATA 61 61 #define HAVE_FT6x06 62 + #define FT6x06_NUM_POINTS 5 62 63 #define HAVE_HEADPHONE_DETECTION 63 64 64 65 /* Storage defines */
+16 -7
firmware/export/ft6x06.h
··· 25 25 #include "config.h" 26 26 #include <stdbool.h> 27 27 28 - typedef void(*ft6x06_event_cb)(int, int, int); 28 + enum ft6x06_event { 29 + FT6x06_EVT_NONE = -1, 30 + FT6x06_EVT_PRESS = 0, 31 + FT6x06_EVT_RELEASE = 1, 32 + FT6x06_EVT_CONTACT = 2, 33 + }; 29 34 30 - struct ft6x06_state { 35 + struct ft6x06_point { 31 36 int event; 37 + int touch_id; 32 38 int pos_x; 33 39 int pos_y; 40 + int weight; 41 + int area; 34 42 }; 35 43 36 - enum ft6x06_event { 37 - FT6x06_EVT_NONE = -1, 38 - FT6x06_EVT_PRESS = 0, 39 - FT6x06_EVT_RELEASE = 1, 40 - FT6x06_EVT_CONTACT = 2, 44 + struct ft6x06_state { 45 + int gesture; 46 + int nr_points; 47 + struct ft6x06_point points[FT6x06_NUM_POINTS]; 41 48 }; 42 49 43 50 extern struct ft6x06_state ft6x06_state; 51 + 52 + typedef void(*ft6x06_event_cb)(struct ft6x06_state* state); 44 53 45 54 void ft6x06_init(void); 46 55 void ft6x06_set_event_cb(ft6x06_event_cb fn);
+6
firmware/target/mips/ingenic_x1000/debug-x1000.c
··· 149 149 #ifdef FIIO_M3K 150 150 extern bool dbg_fiiom3k_touchpad(void); 151 151 #endif 152 + #ifdef SHANLING_Q1 153 + extern bool dbg_shanlingq1_touchscreen(void); 154 + #endif 152 155 #ifdef HAVE_AXP_PMU 153 156 extern bool axp_debug_menu(void); 154 157 #endif ··· 169 172 {"Audio", &dbg_audio}, 170 173 #ifdef FIIO_M3K 171 174 {"Touchpad", &dbg_fiiom3k_touchpad}, 175 + #endif 176 + #ifdef SHANLING_Q1 177 + {"Touchscreen", &dbg_shanlingq1_touchscreen}, 172 178 #endif 173 179 #ifdef HAVE_AXP_PMU 174 180 {"Power stats", &axp_debug_menu},
+3 -2
firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c
··· 318 318 } 319 319 } 320 320 321 - static void ft_event_cb(int evt, int tx, int ty) 321 + static void ft_event_cb(struct ft6x06_state* state) 322 322 { 323 323 /* TODO: convert the touch positions to linear positions. 324 324 * ··· 327 327 * the middle of the touchpad than on the edges, so scrolling feels slow 328 328 * in the middle and faster near the edge. 329 329 */ 330 - ft_step_state(__ost_read32(), evt, tx, ty); 330 + struct ft6x06_point* pt = &state->points[0]; 331 + ft_step_state(__ost_read32(), pt->event, pt->pos_x, pt->pos_y); 331 332 } 332 333 333 334 static void ft_init(void)
+73 -10
firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c
··· 32 32 #include "i2c-x1000.h" 33 33 #include <stdbool.h> 34 34 35 + #ifndef BOOTLOADER 36 + # include "lcd.h" 37 + # include "font.h" 38 + #endif 39 + 35 40 /* Volume wheel rotation */ 36 41 static volatile int wheel_pos = 0; 37 42 ··· 109 114 110 115 int button_read_device(int* data) 111 116 { 117 + const struct ft6x06_point* point; 112 118 int r = 0; 113 119 114 120 /* Read GPIO buttons, these are all active low */ ··· 138 144 reset_poweroff_timer(); 139 145 } 140 146 141 - /* Handle touchscreen 142 - * 143 - * TODO: Support 2-point multitouch (useful for 3x3 grid mode) 144 - * TODO: Support simple gestures by converting them to fake buttons 145 - */ 146 - int t = touchscreen_to_pixels(ft6x06_state.pos_x, ft6x06_state.pos_y, data); 147 - if(ft6x06_state.event == FT6x06_EVT_PRESS || 148 - ft6x06_state.event == FT6x06_EVT_CONTACT) { 149 - /* Only set the button bit if the screen is being touched. */ 150 - r |= t; 147 + if(touchscreen_get_mode() == TOUCHSCREEN_POINT) { 148 + /* Pointing mode can't use multitouch since we can only pass 149 + * along coordinates for one touch event at a time */ 150 + point = &ft6x06_state.points[0]; 151 + int t = touchscreen_to_pixels(point->pos_x, point->pos_y, data); 152 + if(point->event == FT6x06_EVT_PRESS || 153 + point->event == FT6x06_EVT_CONTACT) 154 + r |= t; 155 + } else { 156 + /* 3x3 mode can have simultaneous 'button' presses via multitouch */ 157 + for(int i = 0; i < ft6x06_state.nr_points; ++i) { 158 + point = &ft6x06_state.points[i]; 159 + if(point->event == FT6x06_EVT_PRESS || 160 + point->event == FT6x06_EVT_CONTACT) 161 + r |= touchscreen_to_pixels(point->pos_x, point->pos_y, NULL); 162 + } 151 163 } 152 164 153 165 return r; ··· 193 205 handle_wheel_irq(); 194 206 gpio_flip_edge_irq(GPIO_WHEEL2); 195 207 } 208 + 209 + #ifndef BOOTLOADER 210 + static int getbtn(void) 211 + { 212 + int btn; 213 + do { 214 + btn = button_get_w_tmo(1); 215 + } while(btn & (BUTTON_REL|BUTTON_REPEAT)); 216 + return btn; 217 + } 218 + 219 + bool dbg_shanlingq1_touchscreen(void) 220 + { 221 + /* definition of box used to represent the touchpad */ 222 + const int pad_w = LCD_WIDTH; 223 + const int pad_h = LCD_HEIGHT; 224 + const int box_h = pad_h - SYSFONT_HEIGHT*5; 225 + const int box_w = pad_w * box_h / pad_h; 226 + const int box_x = (LCD_WIDTH - box_w) / 2; 227 + const int box_y = SYSFONT_HEIGHT * 9 / 2; 228 + 229 + bool draw_border = true; 230 + 231 + do { 232 + int line = 0; 233 + lcd_clear_display(); 234 + lcd_putsf(0, line++, "nr_points: %d gesture: %d", 235 + ft6x06_state.nr_points, ft6x06_state.gesture); 236 + 237 + /* draw touchpad box borders */ 238 + if(draw_border) 239 + lcd_drawrect(box_x, box_y, box_w, box_h); 240 + 241 + for(int i = 0; i < ft6x06_state.nr_points; ++i) { 242 + const struct ft6x06_point* point = &ft6x06_state.points[i]; 243 + lcd_putsf(0, line++, "pt%d id:%d pos: %d,%d wgt: %d area:%d", 244 + i, point->touch_id, point->pos_x, point->pos_y, 245 + point->weight, point->area); 246 + 247 + /* draw crosshair */ 248 + int tx = box_x + point->pos_x * box_w / pad_w; 249 + int ty = box_y + point->pos_y * box_h / pad_h; 250 + lcd_hline(tx-2, tx+2, ty); 251 + lcd_vline(tx, ty-2, ty+2); 252 + } 253 + 254 + lcd_update(); 255 + } while(getbtn() != BUTTON_POWER); 256 + return false; 257 + } 258 + #endif