touch: prepare touch-screen driver

This commit is contained in:
Yann MAGNIN 2025-03-17 15:07:29 +01:00
parent 252cb7abd8
commit 3fefc3f716
No known key found for this signature in database
GPG key ID: D82629D933EADC59
9 changed files with 772 additions and 0 deletions

View file

@ -47,4 +47,10 @@ using std::max;
(b) = _tmp; \
})
/* abs() - absolute value of a variable (be careful of neg overflow) */
#define abs(a) ({ \
GAUTOTYPE _a = (a); \
(_a < 0) ? -(_a) : _a; \
})
#endif /* GINT_DEFS_UTIL */

40
include/gint/touch.h Normal file
View file

@ -0,0 +1,40 @@
//---
// gint:touch - touch-screen driver API
//---
#ifndef GINT_TOUCH_H
#define GINT_TOUCH_H 1
#ifdef __cplusplus
extern "C" {
#endif
/* touch_calib - tounch-screen calibration information */
typedef struct {
int scan_freq_us;
int x_mul;
int x_div;
int y_mul;
int y_div;
int dual_debounce_frame;
int dual_sensi_entry;
int dual_sensi_leave;
} touch_calib;
/* touch_calib_get() - get calibration information */
extern int touch_calib_get(touch_calib *calib);
/* touch_calib_set() - set calibration information */
extern int touch_calib_set(touch_calib *calib);
// low-level API
#include <gint/keyboard.h>
/* touch_next_event() - get the next touchscreen event */
extern key_event_t touch_next_event(void);
#ifdef __cplusplus
}
#endif
#endif /* GINT_TOUCH_H */

View file

@ -123,6 +123,7 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
if(code == 0x1080) name = "Stack overflow during world switch";
if(code == 0x10a0) name = "UBC break in register bank 1 code";
if(code == 0x10c0) name = "Missing syscall for this OS version";
if(code == 0x10e0) name = "I2C (touch-screen) error";
dprint(6, 25, "%03x %s", code, name);
@ -173,6 +174,11 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
dprint(6, 160, "Opcodes: %04x %04x [%04x] %04x",
opcodes[-2], opcodes[-1], opcodes[0], opcodes[1]);
}
/* I2C exception error */
if (code == 0x10e0)
{
//todo
}
#endif
_WEAK_dupdate();

114
src/touch/adconv.c Normal file
View file

@ -0,0 +1,114 @@
//---
// gint:touchscreen:adconv - 0x84 register data conversion
//---
#include <gint/defs/util.h>
#include "./adconv.h"
#include "./i2c.h"
//---
// Internals
//---
/* _adconv_check_dual() - check if it is dual or single */
static int _adconv_check_dual(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw
) {
static int _prev_type = 0;
int x2;
int y2;
int z2;
int val;
x2 = ((int)((uint)(adraw->x2) >> 6)) + ((adraw->x2 & 1) * -0x400);
y2 = ((int)((uint)(adraw->y2) >> 6)) + ((adraw->y2 & 1) * -0x400);
z2 = ((int)((uint)(adraw->z2) >> 4)) + ((adraw->z2 & 1) * -0x1000);
adconv->x2 = x2;
adconv->y2 = y2;
adconv->z2 = z2;
val = (_prev_type == 0) ? 0x18 : 0x12;
_prev_type = ((abs(z2) >= val) || (max(abs(x2),abs(y2)) >= val));
return _prev_type;
}
//---
// Public
//---
/* touchscreen_adconv_get_raw() - read 0x84 register using I2C */
int touchscreen_adconv_get_raw(struct _touch_adraw *adraw)
{
volatile uint8_t *IO_PRDR = (void*)0xa405013c;
if (((*IO_PRDR) & 0x20) != 0)
{
adraw->x1 = 0;
adraw->y1 = 0;
adraw->z1 = 0;
adraw->x2 = 0;
adraw->y2 = 0;
adraw->z2 = 0;
adraw->gh = 0;
return 0;
}
i2c_reg_read(0x84, adraw, 16);
return 1;
}
/* touchscreen_adconv_convert() - perform the raw conversion */
int touchscreen_adconv_get_conv(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw,
int type
) {
if (type == 0)
return 0;
adconv->x1 = adraw->x1 >> 4;
adconv->y1 = adraw->y1 >> 4;
adconv->z1 = adraw->z1 >> 4;
adconv->gh = adraw->gh >> 4;
adconv->dm = adraw->dm >> 6;
if (_adconv_check_dual(adconv, adraw) == 0)
return 1;
return 2;
}
/* touchscreen_adconv_get_dots() - generate dots information */
int touchscreen_adconv_get_dots(
struct _touch_addots *dots,
struct _touch_adconv *adconv,
int type
) {
switch (type)
{
case 0:
dots->type = TS_DOTS_TYPE_OFF;
dots->x1 = 0;
dots->y1 = 0;
dots->z1 = 0;
dots->x2 = 0;
dots->y2 = 0;
dots->z2 = 0;
break;
case 1:
dots->type = TS_DOTS_TYPE_SINGLE;
dots->x1 = ((adconv->x1 - 0x1b8) * 0x100) / 0x9e5;
dots->y1 = ((adconv->y1 - 0x104) * 0x100) / 0x660;
dots->z1 = adconv->z1;
dots->x2 = 0;
dots->y2 = 0;
dots->z2 = 0;
break;
case 2:
dots->type = TS_DOTS_TYPE_DUAL;
dots->x1 = ((adconv->x1 - 0x1b8) * 0x100) / 0x9e5;
dots->y1 = ((adconv->y1 - 0x104) * 0x100) / 0x660;
dots->z1 = adconv->z1;
dots->x2 = ((adconv->x2 - 0x1b8) * 0x100) / 0x9e5;
dots->y2 = ((adconv->y2 - 0x104) * 0x100) / 0x660;
dots->z2 = adconv->z2;
break;
}
return type;
}

77
src/touch/adconv.h Normal file
View file

@ -0,0 +1,77 @@
#ifndef GINT_TOUCH_ADCONV_H
#define GINT_TOUCH_ADCONV_H 1
#include <gint/defs/types.h>
//---
// Internals
//---
/* _touch_adraw - raw 0x84 register information */
struct _touch_adraw
{
uint16_t x1;
uint16_t y1;
uint16_t z1;
uint16_t gh;
uint16_t x2;
uint16_t y2;
uint16_t z2;
uint16_t dm;
};
/* _touch_adconv - post-conversion raw 0x84 register information */
struct _touch_adconv
{
int x1;
int y1;
int x2;
int y2;
int z1;
int z2;
uint16_t gh;
uint16_t dm;
};
/* _touch_addots_type - type of dots */
enum _touch_addots_type
{
TS_DOTS_TYPE_OFF = 0,
TS_DOTS_TYPE_SINGLE = 1,
TS_DOTS_TYPE_DUAL = 2,
};
/* _touch_addots - touchscreen dots information */
struct _touch_addots
{
enum _touch_addots_type type;
int x1;
int y1;
int z1;
int x2;
int y2;
int z2;
};
//---
// Public
//---
/* touchscreen_adconv_get_raw() - read 0x84 register using I2C */
extern int touchscreen_adconv_get_raw(struct _touch_adraw *adraw);
/* touchscreen_adconv_get_conv() - perform the raw conversion */
extern int touchscreen_adconv_get_conv(
struct _touch_adconv *adconv,
struct _touch_adraw *adraw,
int type
);
/* touchscreen_adconv_get_dots() - generate dots information */
extern int touchscreen_adconv_get_dots(
struct _touch_addots *dots,
struct _touch_adconv *adconv,
int type
);
#endif /* GINT_TOUCH_ADCONV_H */

186
src/touch/i2c.c Normal file
View file

@ -0,0 +1,186 @@
//---
// gint:touchscreen:i2c - I2C driver
//---
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/defs/call.h>
#include <gint/cpu.h>
#include <gint/intc.h>
#include "./i2c.h"
//---
// Internals
//---
/* __i2c_request - internal I2C request information */
volatile struct i2c_request_info __i2c_request;
/* i2c_hw_enable() - enable the I2C peripheral and configure the clock
*
* notes
* Clock configuration are the same used by Casio. Investigation must be
* made to ensure overclock/underclock validity */
static void _i2c_hw_enable(void)
{
SH7305_I2C.ICCR.ICE = 1;
while(true) {
if (SH7305_I2C.ICSR.BUSY == 0)
break;
}
SH7305_I2C.ICCL = 0x29;
SH7305_I2C.ICCH = 0x22;
}
/* i2c_hw_start_operation() - start I2C operation
*
* notes
* Enable only data transmit and arbitration lost interrupt then perform
* a start condition (0x94) */
static void _i2c_hw_start_operation(void)
{
SH7305_I2C.ICIC.ALE = 1;
SH7305_I2C.ICIC.TACKE = 1;
SH7305_I2C.ICIC.WAITE = 0;
SH7305_I2C.ICIC.DTEE = 1;
__auto_type iccr = SH7305_I2C.ICCR;
iccr.ICE = 1;
iccr.RACK = 0;
iccr.TRS = 1;
iccr.BBSY = 1;
iccr.SCP = 0;
SH7305_I2C.ICCR = iccr;
}
/* i2c_request_await() - await async operation */
static int _i2c_request_await(void)
{
int status;
while (true)
{
cpu_atomic_start();
status = (__i2c_request.status == I2C_REQ_STATUS_FINISHED);
cpu_atomic_end();
if (status)
break;
__asm__ volatile ("sleep");
}
return 0;
}
//---
// Public
//---
/* i2c_power_on() - power on the I2C peripheral and install handlers */
void i2c_power_on(void)
{
volatile uint16_t *INTC_IPRH = (void*)0xa408001c;
volatile uint8_t *INTC_IMCR7 = (void*)0xa40800dc;
volatile uint8_t *INTC_IMR7 = (void*)0xa408009c;
volatile uint16_t *PFC_PJCR = (void*)0xa4050110;
volatile uint32_t *MSTPCR2 = (void*)0xa4150038;
extern void i2c_inth_tack(void);
extern void i2c_inth_wait(void);
extern void i2c_inth_trans(void);
extern void i2c_inth_al(void);
// force-mask I2C interrupts
*INTC_IPRH &= 0xfff0;
*INTC_IMR7 |= 0xf0;
// install I2C handler
intc_handler_function(0xe00, GINT_CALL(i2c_inth_al));
intc_handler_function(0xe20, GINT_CALL(i2c_inth_tack));
intc_handler_function(0xe40, GINT_CALL(i2c_inth_wait));
intc_handler_function(0xe60, GINT_CALL(i2c_inth_trans));
// power-on I2C module
*MSTPCR2 &= 0xfffff7ff;
// configure I2C PIN
*PFC_PJCR &= 0xf0ff;
// configure I2C module
SH7305_I2C.ICCR.byte &= 0x7f;
SH7305_I2C.ICSR.byte = 0x00;
SH7305_I2C.ICIC.byte = 0x00;
// Enable interrupt
*INTC_IPRH |= 1;
*INTC_IMCR7 |= 0xf0;
}
// user API
/* i2c_reg_select() - select a register */
int i2c_reg_select(int reg)
{
cpu_atomic_start();
_i2c_hw_enable();
__i2c_request.mode = I2C_REQ_MODE_REG_SELECT;
__i2c_request.target_register = reg;
__i2c_request.buffer = NULL;
__i2c_request.buffer_size = 0;
__i2c_request.buffer_size_remaning = 0;
__i2c_request.buffer_cursor = 0;
__i2c_request.status = I2C_REQ_STATUS_START;
__i2c_request.state = I2C_REQ_STATE_START;
cpu_atomic_end();
_i2c_hw_start_operation();
return _i2c_request_await();
}
/* i2c_reg_read() - register read operation */
int i2c_reg_read(int reg, void *buffer, size_t size)
{
if (buffer == NULL)
return -1;
if (size == 0)
return -2;
cpu_atomic_start();
_i2c_hw_enable();
__i2c_request.mode = I2C_REQ_MODE_REG_READ;
__i2c_request.target_register = reg;
__i2c_request.buffer = buffer;
__i2c_request.buffer_size = size;
__i2c_request.buffer_size_remaning = size;
__i2c_request.buffer_cursor = 0;
__i2c_request.status = I2C_REQ_STATUS_START;
__i2c_request.state = I2C_REQ_STATE_START;
cpu_atomic_end();
_i2c_hw_start_operation();
return _i2c_request_await();
}
/* i2c_read_stream() - "stream" read operation (skip reg selection) */
int i2c_read_stream(void *buffer, size_t size)
{
if (buffer == NULL)
return -1;
if (size == 0)
return -2;
cpu_atomic_start();
_i2c_hw_enable();
__i2c_request.mode = I2C_REQ_MODE_READ_STREAM;
__i2c_request.target_register = 0x00;
__i2c_request.buffer = buffer;
__i2c_request.buffer_size = size;
__i2c_request.buffer_size_remaning = size;
__i2c_request.buffer_cursor = 0;
__i2c_request.status = I2C_REQ_STATUS_START;
__i2c_request.state = I2C_REQ_STATE_START;
cpu_atomic_end();
_i2c_hw_start_operation();
return _i2c_request_await();
}

124
src/touch/i2c.h Normal file
View file

@ -0,0 +1,124 @@
#ifndef GINT_TOUCH_I2C_H
#define GINT_TOUCH_I2C_H 1
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// User API
//---
/* i2c_power_on() - power on the I2C peripheral and install handlers */
extern void i2c_power_on(void);
/* i2c_reg_select() - select a register */
extern int i2c_reg_select(int reg);
/* i2c_reg_read() - register read operation */
extern int i2c_reg_read(int reg, void *buffer, size_t size);
/* i2c_read_stream() - "stream" read operation (skip reg selection) */
extern int i2c_read_stream(void *buffer, size_t size);
//---
// Internals
//---
/* i2c_request_mode - enumerate request mode */
enum i2c_request_mode {
I2C_REQ_MODE_REG_READ = 0,
I2C_REQ_MODE_REG_WRITE = 1,
I2C_REQ_MODE_REG_SELECT = 2,
I2C_REQ_MODE_READ_STREAM = 3,
};
/* i2c_request_status - request status */
enum i2c_request_status {
I2C_REQ_STATUS_START = 0,
I2C_REQ_STATUS_FINISHED = 1,
};
enum i2c_request_state {
I2C_REQ_STATE_START = 0,
I2C_REQ_STATE_REG_SELECT = 10,
I2C_REQ_STATE_REG_SELECT_WAIT = 11,
I2C_REQ_STATE_SWITCH_TO_RECEIVE = 20,
I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT = 21,
I2C_REQ_STATE_READ = 30,
I2C_REQ_STATE_READ_LAST = 31,
I2C_REQ_STATE_ZOMBIE = 667,
I2C_REQ_STATE_DEAD = 2617,
};
/* struct i2c_request_info - internal I2C request information */
struct i2c_request_info {
enum i2c_request_mode mode;
int target_register;
uint8_t *buffer;
size_t buffer_size;
size_t buffer_size_remaning;
size_t buffer_cursor;
enum i2c_request_status status;
int state;
};
/* __i2c_request - internal current I2C request */
extern volatile struct i2c_request_info __i2c_request;
//---
// Hardware information
//---
typedef struct {
// read/write register
uint8_t ICDR;
pad(3);
// control register
byte_union(ICCR,
uint8_t ICE :1;
uint8_t RACK :1;
uint8_t :1;
uint8_t TRS :1;
uint8_t :1;
uint8_t BBSY :1;
uint8_t :1;
uint8_t SCP :1;
);
pad(3);
// status register
byte_union(ICSR,
uint8_t SCLM :1;
uint8_t SDAM :1;
uint8_t :1;
uint8_t BUSY :1;
uint8_t AL :1;
uint8_t TACK :1;
uint8_t WAIT :1;
uint8_t DTE :1;
);
pad(3);
// interrupt control register
byte_union(ICIC,
uint8_t :1;
uint8_t :1;
uint8_t :1;
uint8_t :1;
uint8_t ALE :1;
uint8_t TACKE :1;
uint8_t WAITE :1;
uint8_t DTEE :1;
);
pad(3);
// clock control registers
uint8_t ICCL;
pad(3);
uint8_t ICCH;
} GPACKED(1) sh7305_i2c_t;
#define SH7305_I2C (*((volatile sh7305_i2c_t *)0xa4470000))
#endif /* CLOVE_TOUCH_I2C_H */

166
src/touch/i2c_inth.c Normal file
View file

@ -0,0 +1,166 @@
//---
// gint:touchscreen:i2c_inth - I2C interrupt handlers
//---
#include <gint/exc.h>
#include "./i2c.h"
//---
// Private
//---
/* _i2c_inth_read() - generic read byte operation */
static int _i2c_inth_io_operation(bool read)
{
if (read) {
__i2c_request.buffer[
__i2c_request.buffer_cursor
] = SH7305_I2C.ICDR;
} else {
SH7305_I2C.ICDR = __i2c_request.buffer[
__i2c_request.buffer_cursor
];
}
__i2c_request.buffer_cursor += 1;
__i2c_request.buffer_size_remaning -= 1;
return __i2c_request.buffer_size_remaning;
}
/* _i2c_inth_stop() - stop I2C module (with force wait) */
static void _i2c_inth_stop(void)
{
while(true) {
if (SH7305_I2C.ICSR.BUSY == 0)
break;
SH7305_I2C.ICDR = SH7305_I2C.ICDR;
}
SH7305_I2C.ICIC.byte = 0x00;
SH7305_I2C.ICSR.byte = 0x00;
SH7305_I2C.ICCR.byte = 0x00;
__i2c_request.state = I2C_REQ_STATE_DEAD;
__i2c_request.status = I2C_REQ_STATUS_FINISHED;
}
//---
// Public
//---
/* i2c_inth_trans() - TRANS interrupt handler */
void i2c_inth_trans(void)
{
switch (__i2c_request.state)
{
// start byte (writing or reading) and select the proper mode
// note that the "read stream" just change the internal state
// because the starting byte (0x81) is force-sended over there
case I2C_REQ_STATE_START:
SH7305_I2C.ICIC.DTEE = 0;
SH7305_I2C.ICIC.WAITE = 1;
if (__i2c_request.mode == I2C_REQ_MODE_READ_STREAM) {
SH7305_I2C.ICDR = 0x81;
__i2c_request.state = I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT;
} else {
SH7305_I2C.ICDR = 0x80;
__i2c_request.state = I2C_REQ_STATE_REG_SELECT;
}
break;
// switch the I2C module to receive mode and setup the next state
// use WAIT interrupt to really switch the I2C to receive
case I2C_REQ_STATE_SWITCH_TO_RECEIVE:
SH7305_I2C.ICIC.DTEE = 0;
SH7305_I2C.ICDR = 0x81;
__i2c_request.state = I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT;
break;
// for the read operation, the only things to do is the ACK at the
// end of the operation
case I2C_REQ_STATE_READ:
SH7305_I2C.ICIC.DTEE = 0;
if (_i2c_inth_io_operation(true) == 0)
_i2c_inth_stop();
break;
// error, unsupported sequences display tests/debug information
default:
gint_panic(0x10e0);
}
}
/* i2c_inth_wait() - WAIT interrupt handler */
void i2c_inth_wait(void)
{
switch (__i2c_request.state)
{
// indicate which register to perform the operation
// we will wait the next WAIT interrupt to ensure that the data has
// been sent
case I2C_REQ_STATE_REG_SELECT:
SH7305_I2C.ICDR = __i2c_request.target_register;
__i2c_request.state = I2C_REQ_STATE_REG_SELECT_WAIT;
SH7305_I2C.ICSR.WAIT = 0;
break;
// the selected register is confirmed, stop or restart
// note that if the "select register" request is performed (or if
// no buffer is provided) then we send the stop condition (0x90)
// and force-stop the module.
// Otherwise, we trigger a new request (0x94) manually to (after
// the restart interruption) switch from "reading" or "writing"
// mode
case I2C_REQ_STATE_REG_SELECT_WAIT:
if (
__i2c_request.mode == I2C_REQ_MODE_REG_SELECT
|| __i2c_request.buffer_size_remaning == 0
|| __i2c_request.buffer == NULL
) {
SH7305_I2C.ICCR.byte = 0x90;
SH7305_I2C.ICSR.WAIT = 0;
_i2c_inth_stop();
} else {
SH7305_I2C.ICCR.byte = 0x94;
__i2c_request.state = I2C_REQ_STATE_SWITCH_TO_RECEIVE;
SH7305_I2C.ICIC.DTEE = 1;
SH7305_I2C.ICSR.WAIT = 0;
}
break;
// switch to receive mode
// waiting the next WAIT interrupt to start reading
case I2C_REQ_STATE_SWITCH_TO_RECEIVE_WAIT:
SH7305_I2C.ICCR.byte = 0x81;
__i2c_request.state = I2C_REQ_STATE_READ;
SH7305_I2C.ICSR.WAIT = 0;
break;
// read operation
// can either be WAIT or DTE interruption, a special case is
// performed to avoid DTE interruption
case I2C_REQ_STATE_READ:
if (__i2c_request.buffer_size_remaning > 1) {
if (SH7305_I2C.ICSR.DTE != 0)
_i2c_inth_io_operation(true);
}
if (__i2c_request.buffer_size_remaning == 1)
SH7305_I2C.ICCR.byte = 0xc0;
SH7305_I2C.ICIC.DTEE = 1;
SH7305_I2C.ICSR.WAIT = 0;
break;
// error cases, unable to handle the WAIT interrupt
default:
gint_panic(0x10e0);
}
}
/* i2c_inth_tack() - TACK interrupt handler */
void i2c_inth_tack(void)
{
gint_panic(0x10e0);
}
/* i2c_inth_al() - AL interrupt handler */
void i2c_inth_al(void)
{
gint_panic(0x10e0);
}

53
src/touch/touch.c Normal file
View file

@ -0,0 +1,53 @@
//---
// gint:touch - touchscreen driver
//----
#include <string.h>
#include <gint/touch.h>
//---
// Internals
//---
touch_calib __ts_calib;
//---
// Public
//---
// user-API
/* touch_calib_get() - get calibration information */
int touch_calib_get(touch_calib *calib)
{
if (calib == NULL)
return -1;
memcpy(calib, &__ts_calib, sizeof(touch_calib));
return 0;
}
/* touch_calib_set() - set calibration information */
int touch_calib_set(touch_calib *calib)
{
if (calib == NULL)
return -1;
memcpy(&__ts_calib, calib, sizeof(touch_calib));
return 0;
}
// low-level API
/* touch_next_event() - get the next touchscreen event */
key_event_t touch_next_event(void)
{
#if 0
struct _ts_adraw adraw;
struct _ts_adconv adconv;
int type;
type = touchscreen_adconv_get_raw(&adraw);
type = touchscreen_adconv_get_conv(&adconv, &adraw, type);
type = touchscreen_adconv_get_dots(dots, &adconv, type);
return type;
#endif
}