diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-20 19:05:16 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-11-24 18:49:03 -0500 |
commit | 44acbc66291da6a8ade8571b73a10e34341a622b (patch) | |
tree | 226f9d9f971756f481455da082443f700bcfc786 /firmware | |
parent | b39acee3abd199d80b84c68ebfa7301b7e7a957e (diff) | |
download | rockbox-44acbc66291da6a8ade8571b73a10e34341a622b.tar.gz rockbox-44acbc66291da6a8ade8571b73a10e34341a622b.zip |
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
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/drivers/ft6x06.c | 64 | ||||
-rw-r--r-- | firmware/export/config/fiiom3k.h | 1 | ||||
-rw-r--r-- | firmware/export/config/shanlingq1.h | 1 | ||||
-rw-r--r-- | firmware/export/ft6x06.h | 23 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/debug-x1000.c | 6 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c | 5 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c | 83 |
7 files changed, 139 insertions, 44 deletions
diff --git a/firmware/drivers/ft6x06.c b/firmware/drivers/ft6x06.c index 538ca10480..c605ee0944 100644 --- a/firmware/drivers/ft6x06.c +++ b/firmware/drivers/ft6x06.c | |||
@@ -24,6 +24,16 @@ | |||
24 | #include "i2c-async.h" | 24 | #include "i2c-async.h" |
25 | #include <string.h> | 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 | struct ft6x06_driver { | 37 | struct ft6x06_driver { |
28 | /* i2c bus data */ | 38 | /* i2c bus data */ |
29 | int i2c_cookie; | 39 | int i2c_cookie; |
@@ -33,39 +43,42 @@ struct ft6x06_driver { | |||
33 | ft6x06_event_cb event_cb; | 43 | ft6x06_event_cb event_cb; |
34 | 44 | ||
35 | /* buffer for I2C transfers */ | 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 | static struct ft6x06_driver ft_drv; | 49 | static struct ft6x06_driver ft_drv; |
40 | struct ft6x06_state ft6x06_state; | 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 | static void ft6x06_i2c_callback(int status, i2c_descriptor* desc) | 63 | static void ft6x06_i2c_callback(int status, i2c_descriptor* desc) |
43 | { | 64 | { |
44 | (void)desc; | 65 | (void)desc; |
45 | if(status != I2C_STATUS_OK) | 66 | if(status != I2C_STATUS_OK) |
46 | return; | 67 | return; |
47 | 68 | ||
48 | int evt = ft_drv.raw_data[1] >> 6; | 69 | ft6x06_state.gesture = ft_drv.raw_data[1]; |
49 | int tx = ft_drv.raw_data[2] | ((ft_drv.raw_data[1] & 0xf) << 8); | 70 | ft6x06_state.nr_points = ft_drv.raw_data[2] & 0xf; |
50 | int ty = ft_drv.raw_data[4] | ((ft_drv.raw_data[3] & 0xf) << 8); | 71 | for(int i = 0; i < FT6x06_NUM_POINTS; ++i) { |
51 | 72 | ft6x06_convert_point(&ft_drv.raw_data[3 + i * BYTES_PER_POINT], | |
52 | ft6x06_state.event = evt; | 73 | &ft6x06_state.points[i]); |
53 | #ifdef FT6x06_SWAP_AXES | 74 | } |
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 | ||
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; | 81 | (void)state; |
67 | (void)tx; | ||
68 | (void)ty; | ||
69 | } | 82 | } |
70 | 83 | ||
71 | void ft6x06_init(void) | 84 | void ft6x06_init(void) |
@@ -74,9 +87,10 @@ void ft6x06_init(void) | |||
74 | memset(&ft_drv, 0, sizeof(ft_drv)); | 87 | memset(&ft_drv, 0, sizeof(ft_drv)); |
75 | ft_drv.event_cb = ft6x06_dummy_event_cb; | 88 | ft_drv.event_cb = ft6x06_dummy_event_cb; |
76 | 89 | ||
77 | ft6x06_state.event = FT6x06_EVT_NONE; | 90 | memset(&ft6x06_state, 0, sizeof(struct ft6x06_state)); |
78 | ft6x06_state.pos_x = 0; | 91 | ft6x06_state.gesture = -1; |
79 | ft6x06_state.pos_y = 0; | 92 | for(int i = 0; i < FT6x06_NUM_POINTS; ++i) |
93 | ft6x06_state.points[i].event = FT6x06_EVT_NONE; | ||
80 | 94 | ||
81 | /* Reserve bus management cookie */ | 95 | /* Reserve bus management cookie */ |
82 | ft_drv.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1); | 96 | ft_drv.i2c_cookie = i2c_async_reserve_cookies(FT6x06_BUS, 1); |
@@ -85,16 +99,16 @@ void ft6x06_init(void) | |||
85 | ft_drv.i2c_desc.slave_addr = FT6x06_ADDR; | 99 | ft_drv.i2c_desc.slave_addr = FT6x06_ADDR; |
86 | ft_drv.i2c_desc.bus_cond = I2C_START | I2C_STOP; | 100 | ft_drv.i2c_desc.bus_cond = I2C_START | I2C_STOP; |
87 | ft_drv.i2c_desc.tran_mode = I2C_READ; | 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 | ft_drv.i2c_desc.count[0] = 1; | 103 | ft_drv.i2c_desc.count[0] = 1; |
90 | ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[0]; | 104 | ft_drv.i2c_desc.buffer[1] = &ft_drv.raw_data[1]; |
91 | ft_drv.i2c_desc.count[1] = 5; | 105 | ft_drv.i2c_desc.count[1] = sizeof(ft_drv.raw_data) - 1; |
92 | ft_drv.i2c_desc.callback = ft6x06_i2c_callback; | 106 | ft_drv.i2c_desc.callback = ft6x06_i2c_callback; |
93 | ft_drv.i2c_desc.arg = 0; | 107 | ft_drv.i2c_desc.arg = 0; |
94 | ft_drv.i2c_desc.next = NULL; | 108 | ft_drv.i2c_desc.next = NULL; |
95 | 109 | ||
96 | /* Set I2C register address */ | 110 | /* Set I2C register address */ |
97 | ft_drv.raw_data[5] = 0x02; | 111 | ft_drv.raw_data[0] = 0x01; |
98 | } | 112 | } |
99 | 113 | ||
100 | void ft6x06_set_event_cb(ft6x06_event_cb cb) | 114 | void ft6x06_set_event_cb(ft6x06_event_cb cb) |
diff --git a/firmware/export/config/fiiom3k.h b/firmware/export/config/fiiom3k.h index 29395bd433..61b6123a67 100644 --- a/firmware/export/config/fiiom3k.h +++ b/firmware/export/config/fiiom3k.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #define HAVE_I2C_ASYNC | 24 | #define HAVE_I2C_ASYNC |
25 | #define HAVE_FT6x06 | 25 | #define HAVE_FT6x06 |
26 | #define FT6x06_SWAP_AXES | 26 | #define FT6x06_SWAP_AXES |
27 | #define FT6x06_NUM_POINTS 1 | ||
27 | 28 | ||
28 | /* Buffer for plugins and codecs. */ | 29 | /* Buffer for plugins and codecs. */ |
29 | #define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */ | 30 | #define PLUGIN_BUFFER_SIZE 0x200000 /* 2 MiB */ |
diff --git a/firmware/export/config/shanlingq1.h b/firmware/export/config/shanlingq1.h index 1f1ee79ca7..16ce888958 100644 --- a/firmware/export/config/shanlingq1.h +++ b/firmware/export/config/shanlingq1.h | |||
@@ -59,6 +59,7 @@ | |||
59 | #define HAVE_TOUCHSCREEN | 59 | #define HAVE_TOUCHSCREEN |
60 | #define HAVE_BUTTON_DATA | 60 | #define HAVE_BUTTON_DATA |
61 | #define HAVE_FT6x06 | 61 | #define HAVE_FT6x06 |
62 | #define FT6x06_NUM_POINTS 5 | ||
62 | #define HAVE_HEADPHONE_DETECTION | 63 | #define HAVE_HEADPHONE_DETECTION |
63 | 64 | ||
64 | /* Storage defines */ | 65 | /* Storage defines */ |
diff --git a/firmware/export/ft6x06.h b/firmware/export/ft6x06.h index de1fdd0979..6596f89272 100644 --- a/firmware/export/ft6x06.h +++ b/firmware/export/ft6x06.h | |||
@@ -25,23 +25,32 @@ | |||
25 | #include "config.h" | 25 | #include "config.h" |
26 | #include <stdbool.h> | 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 | int event; | 36 | int event; |
37 | int touch_id; | ||
32 | int pos_x; | 38 | int pos_x; |
33 | int pos_y; | 39 | int pos_y; |
40 | int weight; | ||
41 | int area; | ||
34 | }; | 42 | }; |
35 | 43 | ||
36 | enum ft6x06_event { | 44 | struct ft6x06_state { |
37 | FT6x06_EVT_NONE = -1, | 45 | int gesture; |
38 | FT6x06_EVT_PRESS = 0, | 46 | int nr_points; |
39 | FT6x06_EVT_RELEASE = 1, | 47 | struct ft6x06_point points[FT6x06_NUM_POINTS]; |
40 | FT6x06_EVT_CONTACT = 2, | ||
41 | }; | 48 | }; |
42 | 49 | ||
43 | extern struct ft6x06_state ft6x06_state; | 50 | extern struct ft6x06_state ft6x06_state; |
44 | 51 | ||
52 | typedef void(*ft6x06_event_cb)(struct ft6x06_state* state); | ||
53 | |||
45 | void ft6x06_init(void); | 54 | void ft6x06_init(void); |
46 | void ft6x06_set_event_cb(ft6x06_event_cb fn); | 55 | void ft6x06_set_event_cb(ft6x06_event_cb fn); |
47 | void ft6x06_enable(bool en); | 56 | void ft6x06_enable(bool en); |
diff --git a/firmware/target/mips/ingenic_x1000/debug-x1000.c b/firmware/target/mips/ingenic_x1000/debug-x1000.c index 1965b0b74e..98b8f95fb5 100644 --- a/firmware/target/mips/ingenic_x1000/debug-x1000.c +++ b/firmware/target/mips/ingenic_x1000/debug-x1000.c | |||
@@ -149,6 +149,9 @@ static bool dbg_cpuidle(void) | |||
149 | #ifdef FIIO_M3K | 149 | #ifdef FIIO_M3K |
150 | extern bool dbg_fiiom3k_touchpad(void); | 150 | extern bool dbg_fiiom3k_touchpad(void); |
151 | #endif | 151 | #endif |
152 | #ifdef SHANLING_Q1 | ||
153 | extern bool dbg_shanlingq1_touchscreen(void); | ||
154 | #endif | ||
152 | #ifdef HAVE_AXP_PMU | 155 | #ifdef HAVE_AXP_PMU |
153 | extern bool axp_debug_menu(void); | 156 | extern bool axp_debug_menu(void); |
154 | #endif | 157 | #endif |
@@ -170,6 +173,9 @@ static const struct { | |||
170 | #ifdef FIIO_M3K | 173 | #ifdef FIIO_M3K |
171 | {"Touchpad", &dbg_fiiom3k_touchpad}, | 174 | {"Touchpad", &dbg_fiiom3k_touchpad}, |
172 | #endif | 175 | #endif |
176 | #ifdef SHANLING_Q1 | ||
177 | {"Touchscreen", &dbg_shanlingq1_touchscreen}, | ||
178 | #endif | ||
173 | #ifdef HAVE_AXP_PMU | 179 | #ifdef HAVE_AXP_PMU |
174 | {"Power stats", &axp_debug_menu}, | 180 | {"Power stats", &axp_debug_menu}, |
175 | #endif | 181 | #endif |
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c index 04e3102d42..24daf2ef69 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/button-fiiom3k.c | |||
@@ -318,7 +318,7 @@ static void ft_step_state(uint32_t t, int evt, int tx, int ty) | |||
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 | /* TODO: convert the touch positions to linear positions. | 323 | /* TODO: convert the touch positions to linear positions. |
324 | * | 324 | * |
@@ -327,7 +327,8 @@ static void ft_event_cb(int evt, int tx, int ty) | |||
327 | * the middle of the touchpad than on the edges, so scrolling feels slow | 327 | * the middle of the touchpad than on the edges, so scrolling feels slow |
328 | * in the middle and faster near the edge. | 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 | static void ft_init(void) | 334 | static void ft_init(void) |
diff --git a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c index 27c49a7bd7..13b0cdd078 100644 --- a/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c +++ b/firmware/target/mips/ingenic_x1000/shanlingq1/button-shanlingq1.c | |||
@@ -32,6 +32,11 @@ | |||
32 | #include "i2c-x1000.h" | 32 | #include "i2c-x1000.h" |
33 | #include <stdbool.h> | 33 | #include <stdbool.h> |
34 | 34 | ||
35 | #ifndef BOOTLOADER | ||
36 | # include "lcd.h" | ||
37 | # include "font.h" | ||
38 | #endif | ||
39 | |||
35 | /* Volume wheel rotation */ | 40 | /* Volume wheel rotation */ |
36 | static volatile int wheel_pos = 0; | 41 | static volatile int wheel_pos = 0; |
37 | 42 | ||
@@ -109,6 +114,7 @@ void button_init_device(void) | |||
109 | 114 | ||
110 | int button_read_device(int* data) | 115 | int button_read_device(int* data) |
111 | { | 116 | { |
117 | const struct ft6x06_point* point; | ||
112 | int r = 0; | 118 | int r = 0; |
113 | 119 | ||
114 | /* Read GPIO buttons, these are all active low */ | 120 | /* Read GPIO buttons, these are all active low */ |
@@ -138,16 +144,22 @@ int button_read_device(int* data) | |||
138 | reset_poweroff_timer(); | 144 | reset_poweroff_timer(); |
139 | } | 145 | } |
140 | 146 | ||
141 | /* Handle touchscreen | 147 | if(touchscreen_get_mode() == TOUCHSCREEN_POINT) { |
142 | * | 148 | /* Pointing mode can't use multitouch since we can only pass |
143 | * TODO: Support 2-point multitouch (useful for 3x3 grid mode) | 149 | * along coordinates for one touch event at a time */ |
144 | * TODO: Support simple gestures by converting them to fake buttons | 150 | point = &ft6x06_state.points[0]; |
145 | */ | 151 | int t = touchscreen_to_pixels(point->pos_x, point->pos_y, data); |
146 | int t = touchscreen_to_pixels(ft6x06_state.pos_x, ft6x06_state.pos_y, data); | 152 | if(point->event == FT6x06_EVT_PRESS || |
147 | if(ft6x06_state.event == FT6x06_EVT_PRESS || | 153 | point->event == FT6x06_EVT_CONTACT) |
148 | ft6x06_state.event == FT6x06_EVT_CONTACT) { | 154 | r |= t; |
149 | /* Only set the button bit if the screen is being touched. */ | 155 | } else { |
150 | r |= t; | 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 | return r; | 165 | return r; |
@@ -193,3 +205,54 @@ void GPIOD03(void) | |||
193 | handle_wheel_irq(); | 205 | handle_wheel_irq(); |
194 | gpio_flip_edge_irq(GPIO_WHEEL2); | 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 | ||