2558 lines
59 KiB
C
2558 lines
59 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller
|
|
*
|
|
* Copyright (C) 2023 Jeff LaBundy <jeff@labundy.com>
|
|
*/
|
|
|
|
#include <linux/bits.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/input.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/input/touchscreen.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/property.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#define IQS7211_PROD_NUM 0x00
|
|
|
|
#define IQS7211_EVENT_MASK_ALL GENMASK(14, 8)
|
|
#define IQS7211_EVENT_MASK_ALP BIT(13)
|
|
#define IQS7211_EVENT_MASK_BTN BIT(12)
|
|
#define IQS7211_EVENT_MASK_ATI BIT(11)
|
|
#define IQS7211_EVENT_MASK_MOVE BIT(10)
|
|
#define IQS7211_EVENT_MASK_GSTR BIT(9)
|
|
#define IQS7211_EVENT_MODE BIT(8)
|
|
|
|
#define IQS7211_COMMS_ERROR 0xEEEE
|
|
#define IQS7211_COMMS_RETRY_MS 50
|
|
#define IQS7211_COMMS_SLEEP_US 100
|
|
#define IQS7211_COMMS_TIMEOUT_US (100 * USEC_PER_MSEC)
|
|
#define IQS7211_RESET_TIMEOUT_MS 150
|
|
#define IQS7211_START_TIMEOUT_US (1 * USEC_PER_SEC)
|
|
|
|
#define IQS7211_NUM_RETRIES 5
|
|
#define IQS7211_NUM_CRX 8
|
|
#define IQS7211_MAX_CTX 13
|
|
|
|
#define IQS7211_MAX_CONTACTS 2
|
|
#define IQS7211_MAX_CYCLES 21
|
|
|
|
/*
|
|
* The following delay is used during instances that must wait for the open-
|
|
* drain RDY pin to settle. Its value is calculated as 5*R*C, where R and C
|
|
* represent typical datasheet values of 4.7k and 100 nF, respectively.
|
|
*/
|
|
#define iqs7211_irq_wait() usleep_range(2500, 2600)
|
|
|
|
enum iqs7211_dev_id {
|
|
IQS7210A,
|
|
IQS7211A,
|
|
IQS7211E,
|
|
};
|
|
|
|
enum iqs7211_comms_mode {
|
|
IQS7211_COMMS_MODE_WAIT,
|
|
IQS7211_COMMS_MODE_FREE,
|
|
IQS7211_COMMS_MODE_FORCE,
|
|
};
|
|
|
|
struct iqs7211_reg_field_desc {
|
|
struct list_head list;
|
|
u8 addr;
|
|
u16 mask;
|
|
u16 val;
|
|
};
|
|
|
|
enum iqs7211_reg_key_id {
|
|
IQS7211_REG_KEY_NONE,
|
|
IQS7211_REG_KEY_PROX,
|
|
IQS7211_REG_KEY_TOUCH,
|
|
IQS7211_REG_KEY_TAP,
|
|
IQS7211_REG_KEY_HOLD,
|
|
IQS7211_REG_KEY_PALM,
|
|
IQS7211_REG_KEY_AXIAL_X,
|
|
IQS7211_REG_KEY_AXIAL_Y,
|
|
IQS7211_REG_KEY_RESERVED
|
|
};
|
|
|
|
enum iqs7211_reg_grp_id {
|
|
IQS7211_REG_GRP_TP,
|
|
IQS7211_REG_GRP_BTN,
|
|
IQS7211_REG_GRP_ALP,
|
|
IQS7211_REG_GRP_SYS,
|
|
IQS7211_NUM_REG_GRPS
|
|
};
|
|
|
|
static const char * const iqs7211_reg_grp_names[IQS7211_NUM_REG_GRPS] = {
|
|
[IQS7211_REG_GRP_TP] = "trackpad",
|
|
[IQS7211_REG_GRP_BTN] = "button",
|
|
[IQS7211_REG_GRP_ALP] = "alp",
|
|
};
|
|
|
|
static const u16 iqs7211_reg_grp_masks[IQS7211_NUM_REG_GRPS] = {
|
|
[IQS7211_REG_GRP_TP] = IQS7211_EVENT_MASK_GSTR,
|
|
[IQS7211_REG_GRP_BTN] = IQS7211_EVENT_MASK_BTN,
|
|
[IQS7211_REG_GRP_ALP] = IQS7211_EVENT_MASK_ALP,
|
|
};
|
|
|
|
struct iqs7211_event_desc {
|
|
const char *name;
|
|
u16 mask;
|
|
u16 enable;
|
|
enum iqs7211_reg_grp_id reg_grp;
|
|
enum iqs7211_reg_key_id reg_key;
|
|
};
|
|
|
|
static const struct iqs7211_event_desc iqs7210a_kp_events[] = {
|
|
{
|
|
.mask = BIT(10),
|
|
.enable = BIT(13) | BIT(12),
|
|
.reg_grp = IQS7211_REG_GRP_ALP,
|
|
},
|
|
{
|
|
.name = "event-prox",
|
|
.mask = BIT(2),
|
|
.enable = BIT(5) | BIT(4),
|
|
.reg_grp = IQS7211_REG_GRP_BTN,
|
|
.reg_key = IQS7211_REG_KEY_PROX,
|
|
},
|
|
{
|
|
.name = "event-touch",
|
|
.mask = BIT(3),
|
|
.enable = BIT(5) | BIT(4),
|
|
.reg_grp = IQS7211_REG_GRP_BTN,
|
|
.reg_key = IQS7211_REG_KEY_TOUCH,
|
|
},
|
|
{
|
|
.name = "event-tap",
|
|
.mask = BIT(0),
|
|
.enable = BIT(0),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
},
|
|
{
|
|
.name = "event-hold",
|
|
.mask = BIT(1),
|
|
.enable = BIT(1),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-neg",
|
|
.mask = BIT(2),
|
|
.enable = BIT(2),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-pos",
|
|
.mask = BIT(3),
|
|
.enable = BIT(3),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-pos",
|
|
.mask = BIT(4),
|
|
.enable = BIT(4),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-neg",
|
|
.mask = BIT(5),
|
|
.enable = BIT(5),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
},
|
|
};
|
|
|
|
static const struct iqs7211_event_desc iqs7211a_kp_events[] = {
|
|
{
|
|
.mask = BIT(14),
|
|
.reg_grp = IQS7211_REG_GRP_ALP,
|
|
},
|
|
{
|
|
.name = "event-tap",
|
|
.mask = BIT(0),
|
|
.enable = BIT(0),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
},
|
|
{
|
|
.name = "event-hold",
|
|
.mask = BIT(1),
|
|
.enable = BIT(1),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-neg",
|
|
.mask = BIT(2),
|
|
.enable = BIT(2),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-pos",
|
|
.mask = BIT(3),
|
|
.enable = BIT(3),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-pos",
|
|
.mask = BIT(4),
|
|
.enable = BIT(4),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-neg",
|
|
.mask = BIT(5),
|
|
.enable = BIT(5),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
},
|
|
};
|
|
|
|
static const struct iqs7211_event_desc iqs7211e_kp_events[] = {
|
|
{
|
|
.mask = BIT(14),
|
|
.reg_grp = IQS7211_REG_GRP_ALP,
|
|
},
|
|
{
|
|
.name = "event-tap",
|
|
.mask = BIT(0),
|
|
.enable = BIT(0),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
},
|
|
{
|
|
.name = "event-tap-double",
|
|
.mask = BIT(1),
|
|
.enable = BIT(1),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
},
|
|
{
|
|
.name = "event-tap-triple",
|
|
.mask = BIT(2),
|
|
.enable = BIT(2),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
},
|
|
{
|
|
.name = "event-hold",
|
|
.mask = BIT(3),
|
|
.enable = BIT(3),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
{
|
|
.name = "event-palm",
|
|
.mask = BIT(4),
|
|
.enable = BIT(4),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_PALM,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-pos",
|
|
.mask = BIT(8),
|
|
.enable = BIT(8),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-neg",
|
|
.mask = BIT(9),
|
|
.enable = BIT(9),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-pos",
|
|
.mask = BIT(10),
|
|
.enable = BIT(10),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-neg",
|
|
.mask = BIT(11),
|
|
.enable = BIT(11),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-pos-hold",
|
|
.mask = BIT(12),
|
|
.enable = BIT(12),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
{
|
|
.name = "event-swipe-x-neg-hold",
|
|
.mask = BIT(13),
|
|
.enable = BIT(13),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-pos-hold",
|
|
.mask = BIT(14),
|
|
.enable = BIT(14),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
{
|
|
.name = "event-swipe-y-neg-hold",
|
|
.mask = BIT(15),
|
|
.enable = BIT(15),
|
|
.reg_grp = IQS7211_REG_GRP_TP,
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
},
|
|
};
|
|
|
|
struct iqs7211_dev_desc {
|
|
const char *tp_name;
|
|
const char *kp_name;
|
|
u16 prod_num;
|
|
u16 show_reset;
|
|
u16 ati_error[IQS7211_NUM_REG_GRPS];
|
|
u16 ati_start[IQS7211_NUM_REG_GRPS];
|
|
u16 suspend;
|
|
u16 ack_reset;
|
|
u16 comms_end;
|
|
u16 comms_req;
|
|
int charge_shift;
|
|
int info_offs;
|
|
int gesture_offs;
|
|
int contact_offs;
|
|
u8 sys_stat;
|
|
u8 sys_ctrl;
|
|
u8 alp_config;
|
|
u8 tp_config;
|
|
u8 exp_file;
|
|
u8 kp_enable[IQS7211_NUM_REG_GRPS];
|
|
u8 gesture_angle;
|
|
u8 rx_tx_map;
|
|
u8 cycle_alloc[2];
|
|
u8 cycle_limit[2];
|
|
const struct iqs7211_event_desc *kp_events;
|
|
int num_kp_events;
|
|
int min_crx_alp;
|
|
int num_ctx;
|
|
};
|
|
|
|
static const struct iqs7211_dev_desc iqs7211_devs[] = {
|
|
[IQS7210A] = {
|
|
.tp_name = "iqs7210a_trackpad",
|
|
.kp_name = "iqs7210a_keys",
|
|
.prod_num = 944,
|
|
.show_reset = BIT(15),
|
|
.ati_error = {
|
|
[IQS7211_REG_GRP_TP] = BIT(12),
|
|
[IQS7211_REG_GRP_BTN] = BIT(0),
|
|
[IQS7211_REG_GRP_ALP] = BIT(8),
|
|
},
|
|
.ati_start = {
|
|
[IQS7211_REG_GRP_TP] = BIT(13),
|
|
[IQS7211_REG_GRP_BTN] = BIT(1),
|
|
[IQS7211_REG_GRP_ALP] = BIT(9),
|
|
},
|
|
.suspend = BIT(11),
|
|
.ack_reset = BIT(7),
|
|
.comms_end = BIT(2),
|
|
.comms_req = BIT(1),
|
|
.charge_shift = 4,
|
|
.info_offs = 0,
|
|
.gesture_offs = 1,
|
|
.contact_offs = 4,
|
|
.sys_stat = 0x0A,
|
|
.sys_ctrl = 0x35,
|
|
.alp_config = 0x39,
|
|
.tp_config = 0x4E,
|
|
.exp_file = 0x57,
|
|
.kp_enable = {
|
|
[IQS7211_REG_GRP_TP] = 0x58,
|
|
[IQS7211_REG_GRP_BTN] = 0x37,
|
|
[IQS7211_REG_GRP_ALP] = 0x37,
|
|
},
|
|
.gesture_angle = 0x5F,
|
|
.rx_tx_map = 0x60,
|
|
.cycle_alloc = { 0x66, 0x75, },
|
|
.cycle_limit = { 10, 6, },
|
|
.kp_events = iqs7210a_kp_events,
|
|
.num_kp_events = ARRAY_SIZE(iqs7210a_kp_events),
|
|
.min_crx_alp = 4,
|
|
.num_ctx = IQS7211_MAX_CTX - 1,
|
|
},
|
|
[IQS7211A] = {
|
|
.tp_name = "iqs7211a_trackpad",
|
|
.kp_name = "iqs7211a_keys",
|
|
.prod_num = 763,
|
|
.show_reset = BIT(7),
|
|
.ati_error = {
|
|
[IQS7211_REG_GRP_TP] = BIT(3),
|
|
[IQS7211_REG_GRP_ALP] = BIT(5),
|
|
},
|
|
.ati_start = {
|
|
[IQS7211_REG_GRP_TP] = BIT(5),
|
|
[IQS7211_REG_GRP_ALP] = BIT(6),
|
|
},
|
|
.ack_reset = BIT(7),
|
|
.comms_req = BIT(4),
|
|
.charge_shift = 0,
|
|
.info_offs = 0,
|
|
.gesture_offs = 1,
|
|
.contact_offs = 4,
|
|
.sys_stat = 0x10,
|
|
.sys_ctrl = 0x50,
|
|
.tp_config = 0x60,
|
|
.alp_config = 0x72,
|
|
.exp_file = 0x74,
|
|
.kp_enable = {
|
|
[IQS7211_REG_GRP_TP] = 0x80,
|
|
},
|
|
.gesture_angle = 0x87,
|
|
.rx_tx_map = 0x90,
|
|
.cycle_alloc = { 0xA0, 0xB0, },
|
|
.cycle_limit = { 10, 8, },
|
|
.kp_events = iqs7211a_kp_events,
|
|
.num_kp_events = ARRAY_SIZE(iqs7211a_kp_events),
|
|
.num_ctx = IQS7211_MAX_CTX - 1,
|
|
},
|
|
[IQS7211E] = {
|
|
.tp_name = "iqs7211e_trackpad",
|
|
.kp_name = "iqs7211e_keys",
|
|
.prod_num = 1112,
|
|
.show_reset = BIT(7),
|
|
.ati_error = {
|
|
[IQS7211_REG_GRP_TP] = BIT(3),
|
|
[IQS7211_REG_GRP_ALP] = BIT(5),
|
|
},
|
|
.ati_start = {
|
|
[IQS7211_REG_GRP_TP] = BIT(5),
|
|
[IQS7211_REG_GRP_ALP] = BIT(6),
|
|
},
|
|
.suspend = BIT(11),
|
|
.ack_reset = BIT(7),
|
|
.comms_end = BIT(6),
|
|
.comms_req = BIT(4),
|
|
.charge_shift = 0,
|
|
.info_offs = 1,
|
|
.gesture_offs = 0,
|
|
.contact_offs = 2,
|
|
.sys_stat = 0x0E,
|
|
.sys_ctrl = 0x33,
|
|
.tp_config = 0x41,
|
|
.alp_config = 0x36,
|
|
.exp_file = 0x4A,
|
|
.kp_enable = {
|
|
[IQS7211_REG_GRP_TP] = 0x4B,
|
|
},
|
|
.gesture_angle = 0x55,
|
|
.rx_tx_map = 0x56,
|
|
.cycle_alloc = { 0x5D, 0x6C, },
|
|
.cycle_limit = { 10, 11, },
|
|
.kp_events = iqs7211e_kp_events,
|
|
.num_kp_events = ARRAY_SIZE(iqs7211e_kp_events),
|
|
.num_ctx = IQS7211_MAX_CTX,
|
|
},
|
|
};
|
|
|
|
struct iqs7211_prop_desc {
|
|
const char *name;
|
|
enum iqs7211_reg_key_id reg_key;
|
|
u8 reg_addr[IQS7211_NUM_REG_GRPS][ARRAY_SIZE(iqs7211_devs)];
|
|
int reg_shift;
|
|
int reg_width;
|
|
int val_pitch;
|
|
int val_min;
|
|
int val_max;
|
|
const char *label;
|
|
};
|
|
|
|
static const struct iqs7211_prop_desc iqs7211_props[] = {
|
|
{
|
|
.name = "azoteq,ati-frac-div-fine",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x1E,
|
|
[IQS7211A] = 0x30,
|
|
[IQS7211E] = 0x21,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x22,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x23,
|
|
[IQS7211A] = 0x36,
|
|
[IQS7211E] = 0x25,
|
|
},
|
|
},
|
|
.reg_shift = 9,
|
|
.reg_width = 5,
|
|
.label = "ATI fine fractional divider",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-frac-mult-coarse",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x1E,
|
|
[IQS7211A] = 0x30,
|
|
[IQS7211E] = 0x21,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x22,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x23,
|
|
[IQS7211A] = 0x36,
|
|
[IQS7211E] = 0x25,
|
|
},
|
|
},
|
|
.reg_shift = 5,
|
|
.reg_width = 4,
|
|
.label = "ATI coarse fractional multiplier",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-frac-div-coarse",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x1E,
|
|
[IQS7211A] = 0x30,
|
|
[IQS7211E] = 0x21,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x22,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x23,
|
|
[IQS7211A] = 0x36,
|
|
[IQS7211E] = 0x25,
|
|
},
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 5,
|
|
.label = "ATI coarse fractional divider",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-comp-div",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x1F,
|
|
[IQS7211E] = 0x22,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x24,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7211E] = 0x26,
|
|
},
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.val_max = 31,
|
|
.label = "ATI compensation divider",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-comp-div",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x24,
|
|
},
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.val_max = 31,
|
|
.label = "ATI compensation divider",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-comp-div",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7211A] = 0x31,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7211A] = 0x37,
|
|
},
|
|
},
|
|
.val_max = 31,
|
|
.label = "ATI compensation divider",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-target",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x20,
|
|
[IQS7211A] = 0x32,
|
|
[IQS7211E] = 0x23,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x27,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x28,
|
|
[IQS7211A] = 0x38,
|
|
[IQS7211E] = 0x27,
|
|
},
|
|
},
|
|
.label = "ATI target",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-base",
|
|
.reg_addr[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x26,
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.val_pitch = 8,
|
|
.label = "ATI base",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-base",
|
|
.reg_addr[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x26,
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.val_pitch = 8,
|
|
.label = "ATI base",
|
|
},
|
|
{
|
|
.name = "azoteq,rate-active-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x29,
|
|
[IQS7211A] = 0x40,
|
|
[IQS7211E] = 0x28,
|
|
},
|
|
.label = "active mode report rate",
|
|
},
|
|
{
|
|
.name = "azoteq,rate-touch-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x2A,
|
|
[IQS7211A] = 0x41,
|
|
[IQS7211E] = 0x29,
|
|
},
|
|
.label = "idle-touch mode report rate",
|
|
},
|
|
{
|
|
.name = "azoteq,rate-idle-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x2B,
|
|
[IQS7211A] = 0x42,
|
|
[IQS7211E] = 0x2A,
|
|
},
|
|
.label = "idle mode report rate",
|
|
},
|
|
{
|
|
.name = "azoteq,rate-lp1-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x2C,
|
|
[IQS7211A] = 0x43,
|
|
[IQS7211E] = 0x2B,
|
|
},
|
|
.label = "low-power mode 1 report rate",
|
|
},
|
|
{
|
|
.name = "azoteq,rate-lp2-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x2D,
|
|
[IQS7211A] = 0x44,
|
|
[IQS7211E] = 0x2C,
|
|
},
|
|
.label = "low-power mode 2 report rate",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-active-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x2E,
|
|
[IQS7211A] = 0x45,
|
|
[IQS7211E] = 0x2D,
|
|
},
|
|
.val_pitch = 1000,
|
|
.label = "active mode timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-touch-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x2F,
|
|
[IQS7211A] = 0x46,
|
|
[IQS7211E] = 0x2E,
|
|
},
|
|
.val_pitch = 1000,
|
|
.label = "idle-touch mode timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-idle-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x30,
|
|
[IQS7211A] = 0x47,
|
|
[IQS7211E] = 0x2F,
|
|
},
|
|
.val_pitch = 1000,
|
|
.label = "idle mode timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-lp1-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x31,
|
|
[IQS7211A] = 0x48,
|
|
[IQS7211E] = 0x30,
|
|
},
|
|
.val_pitch = 1000,
|
|
.label = "low-power mode 1 timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-lp2-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x32,
|
|
[IQS7211E] = 0x31,
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.val_pitch = 1000,
|
|
.val_max = 60000,
|
|
.label = "trackpad reference value update rate",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-lp2-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7211A] = 0x49,
|
|
},
|
|
.val_pitch = 1000,
|
|
.val_max = 60000,
|
|
.label = "trackpad reference value update rate",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-ati-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x32,
|
|
[IQS7211E] = 0x31,
|
|
},
|
|
.reg_width = 8,
|
|
.val_pitch = 1000,
|
|
.val_max = 60000,
|
|
.label = "ATI error timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-ati-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7211A] = 0x35,
|
|
},
|
|
.val_pitch = 1000,
|
|
.val_max = 60000,
|
|
.label = "ATI error timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-comms-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x33,
|
|
[IQS7211A] = 0x4A,
|
|
[IQS7211E] = 0x32,
|
|
},
|
|
.label = "communication timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,timeout-press-ms",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x34,
|
|
},
|
|
.reg_width = 8,
|
|
.val_pitch = 1000,
|
|
.val_max = 60000,
|
|
.label = "press timeout",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-mode",
|
|
.reg_addr[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x37,
|
|
},
|
|
.reg_shift = 15,
|
|
.reg_width = 1,
|
|
.label = "ATI mode",
|
|
},
|
|
{
|
|
.name = "azoteq,ati-mode",
|
|
.reg_addr[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x37,
|
|
},
|
|
.reg_shift = 7,
|
|
.reg_width = 1,
|
|
.label = "ATI mode",
|
|
},
|
|
{
|
|
.name = "azoteq,sense-mode",
|
|
.reg_addr[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x37,
|
|
[IQS7211A] = 0x72,
|
|
[IQS7211E] = 0x36,
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 1,
|
|
.label = "sensing mode",
|
|
},
|
|
{
|
|
.name = "azoteq,sense-mode",
|
|
.reg_addr[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x37,
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 2,
|
|
.val_max = 2,
|
|
.label = "sensing mode",
|
|
},
|
|
{
|
|
.name = "azoteq,fosc-freq",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x38,
|
|
[IQS7211A] = 0x52,
|
|
[IQS7211E] = 0x35,
|
|
},
|
|
.reg_shift = 4,
|
|
.reg_width = 1,
|
|
.label = "core clock frequency selection",
|
|
},
|
|
{
|
|
.name = "azoteq,fosc-trim",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x38,
|
|
[IQS7211A] = 0x52,
|
|
[IQS7211E] = 0x35,
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 4,
|
|
.label = "core clock frequency trim",
|
|
},
|
|
{
|
|
.name = "azoteq,touch-exit",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x3B,
|
|
[IQS7211A] = 0x53,
|
|
[IQS7211E] = 0x38,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x3E,
|
|
},
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.label = "touch exit factor",
|
|
},
|
|
{
|
|
.name = "azoteq,touch-enter",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x3B,
|
|
[IQS7211A] = 0x53,
|
|
[IQS7211E] = 0x38,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x3E,
|
|
},
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.label = "touch entrance factor",
|
|
},
|
|
{
|
|
.name = "azoteq,thresh",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x3C,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x3D,
|
|
[IQS7211A] = 0x54,
|
|
[IQS7211E] = 0x39,
|
|
},
|
|
},
|
|
.label = "threshold",
|
|
},
|
|
{
|
|
.name = "azoteq,debounce-exit",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x3F,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x40,
|
|
[IQS7211A] = 0x56,
|
|
[IQS7211E] = 0x3A,
|
|
},
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.label = "debounce exit factor",
|
|
},
|
|
{
|
|
.name = "azoteq,debounce-enter",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x3F,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x40,
|
|
[IQS7211A] = 0x56,
|
|
[IQS7211E] = 0x3A,
|
|
},
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.label = "debounce entrance factor",
|
|
},
|
|
{
|
|
.name = "azoteq,conv-frac",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x48,
|
|
[IQS7211A] = 0x58,
|
|
[IQS7211E] = 0x3D,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x49,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x4A,
|
|
[IQS7211A] = 0x59,
|
|
[IQS7211E] = 0x3E,
|
|
},
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.label = "conversion frequency fractional divider",
|
|
},
|
|
{
|
|
.name = "azoteq,conv-period",
|
|
.reg_addr = {
|
|
[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x48,
|
|
[IQS7211A] = 0x58,
|
|
[IQS7211E] = 0x3D,
|
|
},
|
|
[IQS7211_REG_GRP_BTN] = {
|
|
[IQS7210A] = 0x49,
|
|
},
|
|
[IQS7211_REG_GRP_ALP] = {
|
|
[IQS7210A] = 0x4A,
|
|
[IQS7211A] = 0x59,
|
|
[IQS7211E] = 0x3E,
|
|
},
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.label = "conversion period",
|
|
},
|
|
{
|
|
.name = "azoteq,thresh",
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x55,
|
|
[IQS7211A] = 0x67,
|
|
[IQS7211E] = 0x48,
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.label = "threshold",
|
|
},
|
|
{
|
|
.name = "azoteq,contact-split",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x55,
|
|
[IQS7211A] = 0x67,
|
|
[IQS7211E] = 0x48,
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.label = "contact split factor",
|
|
},
|
|
{
|
|
.name = "azoteq,trim-x",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x56,
|
|
[IQS7211E] = 0x49,
|
|
},
|
|
.reg_shift = 0,
|
|
.reg_width = 8,
|
|
.label = "horizontal trim width",
|
|
},
|
|
{
|
|
.name = "azoteq,trim-x",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7211A] = 0x68,
|
|
},
|
|
.label = "horizontal trim width",
|
|
},
|
|
{
|
|
.name = "azoteq,trim-y",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7210A] = 0x56,
|
|
[IQS7211E] = 0x49,
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.label = "vertical trim height",
|
|
},
|
|
{
|
|
.name = "azoteq,trim-y",
|
|
.reg_addr[IQS7211_REG_GRP_SYS] = {
|
|
[IQS7211A] = 0x69,
|
|
},
|
|
.label = "vertical trim height",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-max-ms",
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x59,
|
|
[IQS7211A] = 0x81,
|
|
[IQS7211E] = 0x4C,
|
|
},
|
|
.label = "maximum gesture time",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-mid-ms",
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7211E] = 0x4D,
|
|
},
|
|
.label = "repeated gesture time",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-dist",
|
|
.reg_key = IQS7211_REG_KEY_TAP,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5A,
|
|
[IQS7211A] = 0x82,
|
|
[IQS7211E] = 0x4E,
|
|
},
|
|
.label = "gesture distance",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-dist",
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5A,
|
|
[IQS7211A] = 0x82,
|
|
[IQS7211E] = 0x4E,
|
|
},
|
|
.label = "gesture distance",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-min-ms",
|
|
.reg_key = IQS7211_REG_KEY_HOLD,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5B,
|
|
[IQS7211A] = 0x83,
|
|
[IQS7211E] = 0x4F,
|
|
},
|
|
.label = "minimum gesture time",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-max-ms",
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5C,
|
|
[IQS7211A] = 0x84,
|
|
[IQS7211E] = 0x50,
|
|
},
|
|
.label = "maximum gesture time",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-max-ms",
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5C,
|
|
[IQS7211A] = 0x84,
|
|
[IQS7211E] = 0x50,
|
|
},
|
|
.label = "maximum gesture time",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-dist",
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5D,
|
|
[IQS7211A] = 0x85,
|
|
[IQS7211E] = 0x51,
|
|
},
|
|
.label = "gesture distance",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-dist",
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7210A] = 0x5E,
|
|
[IQS7211A] = 0x86,
|
|
[IQS7211E] = 0x52,
|
|
},
|
|
.label = "gesture distance",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-dist-rep",
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_X,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7211E] = 0x53,
|
|
},
|
|
.label = "repeated gesture distance",
|
|
},
|
|
{
|
|
.name = "azoteq,gesture-dist-rep",
|
|
.reg_key = IQS7211_REG_KEY_AXIAL_Y,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7211E] = 0x54,
|
|
},
|
|
.label = "repeated gesture distance",
|
|
},
|
|
{
|
|
.name = "azoteq,thresh",
|
|
.reg_key = IQS7211_REG_KEY_PALM,
|
|
.reg_addr[IQS7211_REG_GRP_TP] = {
|
|
[IQS7211E] = 0x55,
|
|
},
|
|
.reg_shift = 8,
|
|
.reg_width = 8,
|
|
.val_max = 42,
|
|
.label = "threshold",
|
|
},
|
|
};
|
|
|
|
static const u8 iqs7211_gesture_angle[] = {
|
|
0x00, 0x01, 0x02, 0x03,
|
|
0x04, 0x06, 0x07, 0x08,
|
|
0x09, 0x0A, 0x0B, 0x0C,
|
|
0x0E, 0x0F, 0x10, 0x11,
|
|
0x12, 0x14, 0x15, 0x16,
|
|
0x17, 0x19, 0x1A, 0x1B,
|
|
0x1C, 0x1E, 0x1F, 0x21,
|
|
0x22, 0x23, 0x25, 0x26,
|
|
0x28, 0x2A, 0x2B, 0x2D,
|
|
0x2E, 0x30, 0x32, 0x34,
|
|
0x36, 0x38, 0x3A, 0x3C,
|
|
0x3E, 0x40, 0x42, 0x45,
|
|
0x47, 0x4A, 0x4C, 0x4F,
|
|
0x52, 0x55, 0x58, 0x5B,
|
|
0x5F, 0x63, 0x66, 0x6B,
|
|
0x6F, 0x73, 0x78, 0x7E,
|
|
0x83, 0x89, 0x90, 0x97,
|
|
0x9E, 0xA7, 0xB0, 0xBA,
|
|
0xC5, 0xD1, 0xDF, 0xEF,
|
|
};
|
|
|
|
struct iqs7211_ver_info {
|
|
__le16 prod_num;
|
|
__le16 major;
|
|
__le16 minor;
|
|
__le32 patch;
|
|
} __packed;
|
|
|
|
struct iqs7211_touch_data {
|
|
__le16 abs_x;
|
|
__le16 abs_y;
|
|
__le16 pressure;
|
|
__le16 area;
|
|
} __packed;
|
|
|
|
struct iqs7211_tp_config {
|
|
u8 tp_settings;
|
|
u8 total_rx;
|
|
u8 total_tx;
|
|
u8 num_contacts;
|
|
__le16 max_x;
|
|
__le16 max_y;
|
|
} __packed;
|
|
|
|
struct iqs7211_private {
|
|
const struct iqs7211_dev_desc *dev_desc;
|
|
struct gpio_desc *reset_gpio;
|
|
struct gpio_desc *irq_gpio;
|
|
struct i2c_client *client;
|
|
struct input_dev *tp_idev;
|
|
struct input_dev *kp_idev;
|
|
struct iqs7211_ver_info ver_info;
|
|
struct iqs7211_tp_config tp_config;
|
|
struct touchscreen_properties prop;
|
|
struct list_head reg_field_head;
|
|
enum iqs7211_comms_mode comms_init;
|
|
enum iqs7211_comms_mode comms_mode;
|
|
unsigned int num_contacts;
|
|
unsigned int kp_code[ARRAY_SIZE(iqs7211e_kp_events)];
|
|
u8 rx_tx_map[IQS7211_MAX_CTX + 1];
|
|
u8 cycle_alloc[2][33];
|
|
u8 exp_file[2];
|
|
u16 event_mask;
|
|
u16 ati_start;
|
|
u16 gesture_cache;
|
|
};
|
|
|
|
static int iqs7211_irq_poll(struct iqs7211_private *iqs7211, u64 timeout_us)
|
|
{
|
|
int error, val;
|
|
|
|
error = readx_poll_timeout(gpiod_get_value_cansleep, iqs7211->irq_gpio,
|
|
val, val, IQS7211_COMMS_SLEEP_US, timeout_us);
|
|
|
|
return val < 0 ? val : error;
|
|
}
|
|
|
|
static int iqs7211_hard_reset(struct iqs7211_private *iqs7211)
|
|
{
|
|
if (!iqs7211->reset_gpio)
|
|
return 0;
|
|
|
|
gpiod_set_value_cansleep(iqs7211->reset_gpio, 1);
|
|
|
|
/*
|
|
* The following delay ensures the shared RDY/MCLR pin is sampled in
|
|
* between periodic assertions by the device and assumes the default
|
|
* communication timeout has not been overwritten in OTP memory.
|
|
*/
|
|
if (iqs7211->reset_gpio == iqs7211->irq_gpio)
|
|
msleep(IQS7211_RESET_TIMEOUT_MS);
|
|
else
|
|
usleep_range(1000, 1100);
|
|
|
|
gpiod_set_value_cansleep(iqs7211->reset_gpio, 0);
|
|
if (iqs7211->reset_gpio == iqs7211->irq_gpio)
|
|
iqs7211_irq_wait();
|
|
|
|
return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US);
|
|
}
|
|
|
|
static int iqs7211_force_comms(struct iqs7211_private *iqs7211)
|
|
{
|
|
u8 msg_buf[] = { 0xFF, };
|
|
int ret;
|
|
|
|
switch (iqs7211->comms_mode) {
|
|
case IQS7211_COMMS_MODE_WAIT:
|
|
return iqs7211_irq_poll(iqs7211, IQS7211_START_TIMEOUT_US);
|
|
|
|
case IQS7211_COMMS_MODE_FREE:
|
|
return 0;
|
|
|
|
case IQS7211_COMMS_MODE_FORCE:
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* The device cannot communicate until it asserts its interrupt (RDY)
|
|
* pin. Attempts to do so while RDY is deasserted return an ACK; how-
|
|
* ever all write data is ignored, and all read data returns 0xEE.
|
|
*
|
|
* Unsolicited communication must be preceded by a special force com-
|
|
* munication command, after which the device eventually asserts its
|
|
* RDY pin and agrees to communicate.
|
|
*
|
|
* Regardless of whether communication is forced or the result of an
|
|
* interrupt, the device automatically deasserts its RDY pin once it
|
|
* detects an I2C stop condition, or a timeout expires.
|
|
*/
|
|
ret = gpiod_get_value_cansleep(iqs7211->irq_gpio);
|
|
if (ret < 0)
|
|
return ret;
|
|
else if (ret > 0)
|
|
return 0;
|
|
|
|
ret = i2c_master_send(iqs7211->client, msg_buf, sizeof(msg_buf));
|
|
if (ret < (int)sizeof(msg_buf)) {
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
|
|
msleep(IQS7211_COMMS_RETRY_MS);
|
|
return ret;
|
|
}
|
|
|
|
iqs7211_irq_wait();
|
|
|
|
return iqs7211_irq_poll(iqs7211, IQS7211_COMMS_TIMEOUT_US);
|
|
}
|
|
|
|
static int iqs7211_read_burst(struct iqs7211_private *iqs7211,
|
|
u8 reg, void *val, u16 val_len)
|
|
{
|
|
int ret, i;
|
|
struct i2c_client *client = iqs7211->client;
|
|
struct i2c_msg msg[] = {
|
|
{
|
|
.addr = client->addr,
|
|
.flags = 0,
|
|
.len = sizeof(reg),
|
|
.buf = ®,
|
|
},
|
|
{
|
|
.addr = client->addr,
|
|
.flags = I2C_M_RD,
|
|
.len = val_len,
|
|
.buf = (u8 *)val,
|
|
},
|
|
};
|
|
|
|
/*
|
|
* The following loop protects against an edge case in which the RDY
|
|
* pin is automatically deasserted just as the read is initiated. In
|
|
* that case, the read must be retried using forced communication.
|
|
*/
|
|
for (i = 0; i < IQS7211_NUM_RETRIES; i++) {
|
|
ret = iqs7211_force_comms(iqs7211);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
|
|
if (ret < (int)ARRAY_SIZE(msg)) {
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
|
|
msleep(IQS7211_COMMS_RETRY_MS);
|
|
continue;
|
|
}
|
|
|
|
if (get_unaligned_le16(msg[1].buf) == IQS7211_COMMS_ERROR) {
|
|
ret = -ENODATA;
|
|
continue;
|
|
}
|
|
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
iqs7211_irq_wait();
|
|
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Failed to read from address 0x%02X: %d\n", reg, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int iqs7211_read_word(struct iqs7211_private *iqs7211, u8 reg, u16 *val)
|
|
{
|
|
__le16 val_buf;
|
|
int error;
|
|
|
|
error = iqs7211_read_burst(iqs7211, reg, &val_buf, sizeof(val_buf));
|
|
if (error)
|
|
return error;
|
|
|
|
*val = le16_to_cpu(val_buf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iqs7211_write_burst(struct iqs7211_private *iqs7211,
|
|
u8 reg, const void *val, u16 val_len)
|
|
{
|
|
int msg_len = sizeof(reg) + val_len;
|
|
int ret, i;
|
|
struct i2c_client *client = iqs7211->client;
|
|
u8 *msg_buf;
|
|
|
|
msg_buf = kzalloc(msg_len, GFP_KERNEL);
|
|
if (!msg_buf)
|
|
return -ENOMEM;
|
|
|
|
*msg_buf = reg;
|
|
memcpy(msg_buf + sizeof(reg), val, val_len);
|
|
|
|
/*
|
|
* The following loop protects against an edge case in which the RDY
|
|
* pin is automatically asserted just before the force communication
|
|
* command is sent.
|
|
*
|
|
* In that case, the subsequent I2C stop condition tricks the device
|
|
* into preemptively deasserting the RDY pin and the command must be
|
|
* sent again.
|
|
*/
|
|
for (i = 0; i < IQS7211_NUM_RETRIES; i++) {
|
|
ret = iqs7211_force_comms(iqs7211);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = i2c_master_send(client, msg_buf, msg_len);
|
|
if (ret < msg_len) {
|
|
if (ret >= 0)
|
|
ret = -EIO;
|
|
|
|
msleep(IQS7211_COMMS_RETRY_MS);
|
|
continue;
|
|
}
|
|
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
kfree(msg_buf);
|
|
|
|
iqs7211_irq_wait();
|
|
|
|
if (ret < 0)
|
|
dev_err(&client->dev,
|
|
"Failed to write to address 0x%02X: %d\n", reg, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int iqs7211_write_word(struct iqs7211_private *iqs7211, u8 reg, u16 val)
|
|
{
|
|
__le16 val_buf = cpu_to_le16(val);
|
|
|
|
return iqs7211_write_burst(iqs7211, reg, &val_buf, sizeof(val_buf));
|
|
}
|
|
|
|
static int iqs7211_start_comms(struct iqs7211_private *iqs7211)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct i2c_client *client = iqs7211->client;
|
|
bool forced_comms;
|
|
unsigned int val;
|
|
u16 comms_setup;
|
|
int error;
|
|
|
|
/*
|
|
* Until forced communication can be enabled, the host must wait for a
|
|
* communication window each time it intends to elicit a response from
|
|
* the device.
|
|
*
|
|
* Forced communication is not necessary, however, if the host adapter
|
|
* can support clock stretching. In that case, the device freely clock
|
|
* stretches until all pending conversions are complete.
|
|
*/
|
|
forced_comms = device_property_present(&client->dev,
|
|
"azoteq,forced-comms");
|
|
|
|
error = device_property_read_u32(&client->dev,
|
|
"azoteq,forced-comms-default", &val);
|
|
if (error == -EINVAL) {
|
|
iqs7211->comms_init = IQS7211_COMMS_MODE_WAIT;
|
|
} else if (error) {
|
|
dev_err(&client->dev,
|
|
"Failed to read default communication mode: %d\n",
|
|
error);
|
|
return error;
|
|
} else if (val) {
|
|
iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_FORCE
|
|
: IQS7211_COMMS_MODE_WAIT;
|
|
} else {
|
|
iqs7211->comms_init = forced_comms ? IQS7211_COMMS_MODE_WAIT
|
|
: IQS7211_COMMS_MODE_FREE;
|
|
}
|
|
|
|
iqs7211->comms_mode = iqs7211->comms_init;
|
|
|
|
error = iqs7211_hard_reset(iqs7211);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to reset device: %d\n", error);
|
|
return error;
|
|
}
|
|
|
|
error = iqs7211_read_burst(iqs7211, IQS7211_PROD_NUM,
|
|
&iqs7211->ver_info,
|
|
sizeof(iqs7211->ver_info));
|
|
if (error)
|
|
return error;
|
|
|
|
if (le16_to_cpu(iqs7211->ver_info.prod_num) != dev_desc->prod_num) {
|
|
dev_err(&client->dev, "Invalid product number: %u\n",
|
|
le16_to_cpu(iqs7211->ver_info.prod_num));
|
|
return -EINVAL;
|
|
}
|
|
|
|
error = iqs7211_read_word(iqs7211, dev_desc->sys_ctrl + 1,
|
|
&comms_setup);
|
|
if (error)
|
|
return error;
|
|
|
|
if (forced_comms)
|
|
comms_setup |= dev_desc->comms_req;
|
|
else
|
|
comms_setup &= ~dev_desc->comms_req;
|
|
|
|
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
|
|
comms_setup | dev_desc->comms_end);
|
|
if (error)
|
|
return error;
|
|
|
|
if (forced_comms)
|
|
iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE;
|
|
else
|
|
iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE;
|
|
|
|
error = iqs7211_read_burst(iqs7211, dev_desc->exp_file,
|
|
iqs7211->exp_file,
|
|
sizeof(iqs7211->exp_file));
|
|
if (error)
|
|
return error;
|
|
|
|
error = iqs7211_read_burst(iqs7211, dev_desc->tp_config,
|
|
&iqs7211->tp_config,
|
|
sizeof(iqs7211->tp_config));
|
|
if (error)
|
|
return error;
|
|
|
|
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
|
|
comms_setup);
|
|
if (error)
|
|
return error;
|
|
|
|
iqs7211->event_mask = comms_setup & ~IQS7211_EVENT_MASK_ALL;
|
|
iqs7211->event_mask |= (IQS7211_EVENT_MASK_ATI | IQS7211_EVENT_MODE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iqs7211_init_device(struct iqs7211_private *iqs7211)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct iqs7211_reg_field_desc *reg_field;
|
|
__le16 sys_ctrl[] = {
|
|
cpu_to_le16(dev_desc->ack_reset),
|
|
cpu_to_le16(iqs7211->event_mask),
|
|
};
|
|
int error, i;
|
|
|
|
/*
|
|
* Acknowledge reset before writing any registers in case the device
|
|
* suffers a spurious reset during initialization. The communication
|
|
* mode is configured at this time as well.
|
|
*/
|
|
error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
|
|
sizeof(sys_ctrl));
|
|
if (error)
|
|
return error;
|
|
|
|
if (iqs7211->event_mask & dev_desc->comms_req)
|
|
iqs7211->comms_mode = IQS7211_COMMS_MODE_FORCE;
|
|
else
|
|
iqs7211->comms_mode = IQS7211_COMMS_MODE_FREE;
|
|
|
|
/*
|
|
* Take advantage of the stop-bit disable function, if available, to
|
|
* save the trouble of having to reopen a communication window after
|
|
* each read or write.
|
|
*/
|
|
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl + 1,
|
|
iqs7211->event_mask | dev_desc->comms_end);
|
|
if (error)
|
|
return error;
|
|
|
|
list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) {
|
|
u16 new_val = reg_field->val;
|
|
|
|
if (reg_field->mask < U16_MAX) {
|
|
u16 old_val;
|
|
|
|
error = iqs7211_read_word(iqs7211, reg_field->addr,
|
|
&old_val);
|
|
if (error)
|
|
return error;
|
|
|
|
new_val = old_val & ~reg_field->mask;
|
|
new_val |= reg_field->val;
|
|
|
|
if (new_val == old_val)
|
|
continue;
|
|
}
|
|
|
|
error = iqs7211_write_word(iqs7211, reg_field->addr, new_val);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
error = iqs7211_write_burst(iqs7211, dev_desc->tp_config,
|
|
&iqs7211->tp_config,
|
|
sizeof(iqs7211->tp_config));
|
|
if (error)
|
|
return error;
|
|
|
|
if (**iqs7211->cycle_alloc) {
|
|
error = iqs7211_write_burst(iqs7211, dev_desc->rx_tx_map,
|
|
&iqs7211->rx_tx_map,
|
|
dev_desc->num_ctx);
|
|
if (error)
|
|
return error;
|
|
|
|
for (i = 0; i < sizeof(dev_desc->cycle_limit); i++) {
|
|
error = iqs7211_write_burst(iqs7211,
|
|
dev_desc->cycle_alloc[i],
|
|
iqs7211->cycle_alloc[i],
|
|
dev_desc->cycle_limit[i] * 3);
|
|
if (error)
|
|
return error;
|
|
}
|
|
}
|
|
|
|
*sys_ctrl = cpu_to_le16(iqs7211->ati_start);
|
|
|
|
return iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
|
|
sizeof(sys_ctrl));
|
|
}
|
|
|
|
static int iqs7211_add_field(struct iqs7211_private *iqs7211,
|
|
struct iqs7211_reg_field_desc new_field)
|
|
{
|
|
struct i2c_client *client = iqs7211->client;
|
|
struct iqs7211_reg_field_desc *reg_field;
|
|
|
|
if (!new_field.addr)
|
|
return 0;
|
|
|
|
list_for_each_entry(reg_field, &iqs7211->reg_field_head, list) {
|
|
if (reg_field->addr != new_field.addr)
|
|
continue;
|
|
|
|
reg_field->mask |= new_field.mask;
|
|
reg_field->val |= new_field.val;
|
|
return 0;
|
|
}
|
|
|
|
reg_field = devm_kzalloc(&client->dev, sizeof(*reg_field), GFP_KERNEL);
|
|
if (!reg_field)
|
|
return -ENOMEM;
|
|
|
|
reg_field->addr = new_field.addr;
|
|
reg_field->mask = new_field.mask;
|
|
reg_field->val = new_field.val;
|
|
|
|
list_add(®_field->list, &iqs7211->reg_field_head);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iqs7211_parse_props(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *reg_grp_node,
|
|
enum iqs7211_reg_grp_id reg_grp,
|
|
enum iqs7211_reg_key_id reg_key)
|
|
{
|
|
struct i2c_client *client = iqs7211->client;
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(iqs7211_props); i++) {
|
|
const char *name = iqs7211_props[i].name;
|
|
u8 reg_addr = iqs7211_props[i].reg_addr[reg_grp]
|
|
[iqs7211->dev_desc -
|
|
iqs7211_devs];
|
|
int reg_shift = iqs7211_props[i].reg_shift;
|
|
int reg_width = iqs7211_props[i].reg_width ? : 16;
|
|
int val_pitch = iqs7211_props[i].val_pitch ? : 1;
|
|
int val_min = iqs7211_props[i].val_min;
|
|
int val_max = iqs7211_props[i].val_max;
|
|
const char *label = iqs7211_props[i].label ? : name;
|
|
struct iqs7211_reg_field_desc reg_field;
|
|
unsigned int val;
|
|
int error;
|
|
|
|
if (iqs7211_props[i].reg_key != reg_key)
|
|
continue;
|
|
|
|
if (!reg_addr)
|
|
continue;
|
|
|
|
error = fwnode_property_read_u32(reg_grp_node, name, &val);
|
|
if (error == -EINVAL) {
|
|
continue;
|
|
} else if (error) {
|
|
dev_err(&client->dev, "Failed to read %s %s: %d\n",
|
|
fwnode_get_name(reg_grp_node), label, error);
|
|
return error;
|
|
}
|
|
|
|
if (!val_max)
|
|
val_max = GENMASK(reg_width - 1, 0) * val_pitch;
|
|
|
|
if (val < val_min || val > val_max) {
|
|
dev_err(&client->dev, "Invalid %s: %u\n", label, val);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_field.addr = reg_addr;
|
|
reg_field.mask = GENMASK(reg_shift + reg_width - 1, reg_shift);
|
|
reg_field.val = val / val_pitch << reg_shift;
|
|
|
|
error = iqs7211_add_field(iqs7211, reg_field);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iqs7211_parse_event(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *event_node,
|
|
enum iqs7211_reg_grp_id reg_grp,
|
|
enum iqs7211_reg_key_id reg_key,
|
|
unsigned int *event_code)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct i2c_client *client = iqs7211->client;
|
|
struct iqs7211_reg_field_desc reg_field;
|
|
unsigned int val;
|
|
int error;
|
|
|
|
error = iqs7211_parse_props(iqs7211, event_node, reg_grp, reg_key);
|
|
if (error)
|
|
return error;
|
|
|
|
if (reg_key == IQS7211_REG_KEY_AXIAL_X ||
|
|
reg_key == IQS7211_REG_KEY_AXIAL_Y) {
|
|
error = fwnode_property_read_u32(event_node,
|
|
"azoteq,gesture-angle", &val);
|
|
if (!error) {
|
|
if (val >= ARRAY_SIZE(iqs7211_gesture_angle)) {
|
|
dev_err(&client->dev,
|
|
"Invalid %s gesture angle: %u\n",
|
|
fwnode_get_name(event_node), val);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_field.addr = dev_desc->gesture_angle;
|
|
reg_field.mask = U8_MAX;
|
|
reg_field.val = iqs7211_gesture_angle[val];
|
|
|
|
error = iqs7211_add_field(iqs7211, reg_field);
|
|
if (error)
|
|
return error;
|
|
} else if (error != -EINVAL) {
|
|
dev_err(&client->dev,
|
|
"Failed to read %s gesture angle: %d\n",
|
|
fwnode_get_name(event_node), error);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
error = fwnode_property_read_u32(event_node, "linux,code", event_code);
|
|
if (error == -EINVAL)
|
|
error = 0;
|
|
else if (error)
|
|
dev_err(&client->dev, "Failed to read %s code: %d\n",
|
|
fwnode_get_name(event_node), error);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int iqs7211_parse_cycles(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *tp_node)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct i2c_client *client = iqs7211->client;
|
|
int num_cycles = dev_desc->cycle_limit[0] + dev_desc->cycle_limit[1];
|
|
int error, count, i, j, k, cycle_start;
|
|
unsigned int cycle_alloc[IQS7211_MAX_CYCLES][2];
|
|
u8 total_rx = iqs7211->tp_config.total_rx;
|
|
u8 total_tx = iqs7211->tp_config.total_tx;
|
|
|
|
for (i = 0; i < IQS7211_MAX_CYCLES * 2; i++)
|
|
*(cycle_alloc[0] + i) = U8_MAX;
|
|
|
|
count = fwnode_property_count_u32(tp_node, "azoteq,channel-select");
|
|
if (count == -EINVAL) {
|
|
/*
|
|
* Assign each sensing cycle's slots (0 and 1) to a channel,
|
|
* defined as the intersection between two CRx and CTx pins.
|
|
* A channel assignment of 255 means the slot is unused.
|
|
*/
|
|
for (i = 0, cycle_start = 0; i < total_tx; i++) {
|
|
int cycle_stop = 0;
|
|
|
|
for (j = 0; j < total_rx; j++) {
|
|
/*
|
|
* Channels formed by CRx0-3 and CRx4-7 are
|
|
* bound to slots 0 and 1, respectively.
|
|
*/
|
|
int slot = iqs7211->rx_tx_map[j] < 4 ? 0 : 1;
|
|
int chan = i * total_rx + j;
|
|
|
|
for (k = cycle_start; k < num_cycles; k++) {
|
|
if (cycle_alloc[k][slot] < U8_MAX)
|
|
continue;
|
|
|
|
cycle_alloc[k][slot] = chan;
|
|
break;
|
|
}
|
|
|
|
if (k < num_cycles) {
|
|
cycle_stop = max(k, cycle_stop);
|
|
continue;
|
|
}
|
|
|
|
dev_err(&client->dev,
|
|
"Insufficient number of cycles\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Sensing cycles cannot straddle more than one CTx
|
|
* pin. As such, the next row's starting cycle must
|
|
* be greater than the previous row's highest cycle.
|
|
*/
|
|
cycle_start = cycle_stop + 1;
|
|
}
|
|
} else if (count < 0) {
|
|
dev_err(&client->dev, "Failed to count channels: %d\n", count);
|
|
return count;
|
|
} else if (count > num_cycles * 2) {
|
|
dev_err(&client->dev, "Insufficient number of cycles\n");
|
|
return -EINVAL;
|
|
} else if (count > 0) {
|
|
error = fwnode_property_read_u32_array(tp_node,
|
|
"azoteq,channel-select",
|
|
cycle_alloc[0], count);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to read channels: %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
int chan = *(cycle_alloc[0] + i);
|
|
|
|
if (chan == U8_MAX)
|
|
continue;
|
|
|
|
if (chan >= total_rx * total_tx) {
|
|
dev_err(&client->dev, "Invalid channel: %d\n",
|
|
chan);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (j = 0; j < count; j++) {
|
|
if (j == i || *(cycle_alloc[0] + j) != chan)
|
|
continue;
|
|
|
|
dev_err(&client->dev, "Duplicate channel: %d\n",
|
|
chan);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Once the raw channel assignments have been derived, they must be
|
|
* packed according to the device's register map.
|
|
*/
|
|
for (i = 0, cycle_start = 0; i < sizeof(dev_desc->cycle_limit); i++) {
|
|
int offs = 0;
|
|
|
|
for (j = cycle_start;
|
|
j < cycle_start + dev_desc->cycle_limit[i]; j++) {
|
|
iqs7211->cycle_alloc[i][offs++] = 0x05;
|
|
iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][0];
|
|
iqs7211->cycle_alloc[i][offs++] = cycle_alloc[j][1];
|
|
}
|
|
|
|
cycle_start += dev_desc->cycle_limit[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int iqs7211_parse_tp(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *tp_node)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct i2c_client *client = iqs7211->client;
|
|
unsigned int pins[IQS7211_MAX_CTX];
|
|
int error, count, i, j;
|
|
|
|
count = fwnode_property_count_u32(tp_node, "azoteq,rx-enable");
|
|
if (count == -EINVAL) {
|
|
return 0;
|
|
} else if (count < 0) {
|
|
dev_err(&client->dev, "Failed to count CRx pins: %d\n", count);
|
|
return count;
|
|
} else if (count > IQS7211_NUM_CRX) {
|
|
dev_err(&client->dev, "Invalid number of CRx pins\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
error = fwnode_property_read_u32_array(tp_node, "azoteq,rx-enable",
|
|
pins, count);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to read CRx pins: %d\n", error);
|
|
return error;
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (pins[i] >= IQS7211_NUM_CRX) {
|
|
dev_err(&client->dev, "Invalid CRx pin: %u\n", pins[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iqs7211->rx_tx_map[i] = pins[i];
|
|
}
|
|
|
|
iqs7211->tp_config.total_rx = count;
|
|
|
|
count = fwnode_property_count_u32(tp_node, "azoteq,tx-enable");
|
|
if (count < 0) {
|
|
dev_err(&client->dev, "Failed to count CTx pins: %d\n", count);
|
|
return count;
|
|
} else if (count > dev_desc->num_ctx) {
|
|
dev_err(&client->dev, "Invalid number of CTx pins\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
error = fwnode_property_read_u32_array(tp_node, "azoteq,tx-enable",
|
|
pins, count);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to read CTx pins: %d\n", error);
|
|
return error;
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (pins[i] >= dev_desc->num_ctx) {
|
|
dev_err(&client->dev, "Invalid CTx pin: %u\n", pins[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (j = 0; j < iqs7211->tp_config.total_rx; j++) {
|
|
if (iqs7211->rx_tx_map[j] != pins[i])
|
|
continue;
|
|
|
|
dev_err(&client->dev, "Conflicting CTx pin: %u\n",
|
|
pins[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iqs7211->rx_tx_map[iqs7211->tp_config.total_rx + i] = pins[i];
|
|
}
|
|
|
|
iqs7211->tp_config.total_tx = count;
|
|
|
|
return iqs7211_parse_cycles(iqs7211, tp_node);
|
|
}
|
|
|
|
static int iqs7211_parse_alp(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *alp_node)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct i2c_client *client = iqs7211->client;
|
|
struct iqs7211_reg_field_desc reg_field;
|
|
int error, count, i;
|
|
|
|
count = fwnode_property_count_u32(alp_node, "azoteq,rx-enable");
|
|
if (count < 0 && count != -EINVAL) {
|
|
dev_err(&client->dev, "Failed to count CRx pins: %d\n", count);
|
|
return count;
|
|
} else if (count > IQS7211_NUM_CRX) {
|
|
dev_err(&client->dev, "Invalid number of CRx pins\n");
|
|
return -EINVAL;
|
|
} else if (count >= 0) {
|
|
unsigned int pins[IQS7211_NUM_CRX];
|
|
|
|
error = fwnode_property_read_u32_array(alp_node,
|
|
"azoteq,rx-enable",
|
|
pins, count);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to read CRx pins: %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
reg_field.addr = dev_desc->alp_config;
|
|
reg_field.mask = GENMASK(IQS7211_NUM_CRX - 1, 0);
|
|
reg_field.val = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (pins[i] < dev_desc->min_crx_alp ||
|
|
pins[i] >= IQS7211_NUM_CRX) {
|
|
dev_err(&client->dev, "Invalid CRx pin: %u\n",
|
|
pins[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_field.val |= BIT(pins[i]);
|
|
}
|
|
|
|
error = iqs7211_add_field(iqs7211, reg_field);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
count = fwnode_property_count_u32(alp_node, "azoteq,tx-enable");
|
|
if (count < 0 && count != -EINVAL) {
|
|
dev_err(&client->dev, "Failed to count CTx pins: %d\n", count);
|
|
return count;
|
|
} else if (count > dev_desc->num_ctx) {
|
|
dev_err(&client->dev, "Invalid number of CTx pins\n");
|
|
return -EINVAL;
|
|
} else if (count >= 0) {
|
|
unsigned int pins[IQS7211_MAX_CTX];
|
|
|
|
error = fwnode_property_read_u32_array(alp_node,
|
|
"azoteq,tx-enable",
|
|
pins, count);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to read CTx pins: %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
reg_field.addr = dev_desc->alp_config + 1;
|
|
reg_field.mask = GENMASK(dev_desc->num_ctx - 1, 0);
|
|
reg_field.val = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (pins[i] >= dev_desc->num_ctx) {
|
|
dev_err(&client->dev, "Invalid CTx pin: %u\n",
|
|
pins[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reg_field.val |= BIT(pins[i]);
|
|
}
|
|
|
|
error = iqs7211_add_field(iqs7211, reg_field);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int (*iqs7211_parse_extra[IQS7211_NUM_REG_GRPS])
|
|
(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *reg_grp_node) = {
|
|
[IQS7211_REG_GRP_TP] = iqs7211_parse_tp,
|
|
[IQS7211_REG_GRP_ALP] = iqs7211_parse_alp,
|
|
};
|
|
|
|
static int iqs7211_parse_reg_grp(struct iqs7211_private *iqs7211,
|
|
struct fwnode_handle *reg_grp_node,
|
|
enum iqs7211_reg_grp_id reg_grp)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct iqs7211_reg_field_desc reg_field;
|
|
int error, i;
|
|
|
|
error = iqs7211_parse_props(iqs7211, reg_grp_node, reg_grp,
|
|
IQS7211_REG_KEY_NONE);
|
|
if (error)
|
|
return error;
|
|
|
|
if (iqs7211_parse_extra[reg_grp]) {
|
|
error = iqs7211_parse_extra[reg_grp](iqs7211, reg_grp_node);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
iqs7211->ati_start |= dev_desc->ati_start[reg_grp];
|
|
|
|
reg_field.addr = dev_desc->kp_enable[reg_grp];
|
|
reg_field.mask = 0;
|
|
reg_field.val = 0;
|
|
|
|
for (i = 0; i < dev_desc->num_kp_events; i++) {
|
|
const char *event_name = dev_desc->kp_events[i].name;
|
|
struct fwnode_handle *event_node;
|
|
|
|
if (dev_desc->kp_events[i].reg_grp != reg_grp)
|
|
continue;
|
|
|
|
reg_field.mask |= dev_desc->kp_events[i].enable;
|
|
|
|
if (event_name)
|
|
event_node = fwnode_get_named_child_node(reg_grp_node,
|
|
event_name);
|
|
else
|
|
event_node = fwnode_handle_get(reg_grp_node);
|
|
|
|
if (!event_node)
|
|
continue;
|
|
|
|
error = iqs7211_parse_event(iqs7211, event_node,
|
|
dev_desc->kp_events[i].reg_grp,
|
|
dev_desc->kp_events[i].reg_key,
|
|
&iqs7211->kp_code[i]);
|
|
fwnode_handle_put(event_node);
|
|
if (error)
|
|
return error;
|
|
|
|
reg_field.val |= dev_desc->kp_events[i].enable;
|
|
|
|
iqs7211->event_mask |= iqs7211_reg_grp_masks[reg_grp];
|
|
}
|
|
|
|
return iqs7211_add_field(iqs7211, reg_field);
|
|
}
|
|
|
|
static int iqs7211_register_kp(struct iqs7211_private *iqs7211)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct input_dev *kp_idev = iqs7211->kp_idev;
|
|
struct i2c_client *client = iqs7211->client;
|
|
int error, i;
|
|
|
|
for (i = 0; i < dev_desc->num_kp_events; i++)
|
|
if (iqs7211->kp_code[i])
|
|
break;
|
|
|
|
if (i == dev_desc->num_kp_events)
|
|
return 0;
|
|
|
|
kp_idev = devm_input_allocate_device(&client->dev);
|
|
if (!kp_idev)
|
|
return -ENOMEM;
|
|
|
|
iqs7211->kp_idev = kp_idev;
|
|
|
|
kp_idev->name = dev_desc->kp_name;
|
|
kp_idev->id.bustype = BUS_I2C;
|
|
|
|
for (i = 0; i < dev_desc->num_kp_events; i++)
|
|
if (iqs7211->kp_code[i])
|
|
input_set_capability(iqs7211->kp_idev, EV_KEY,
|
|
iqs7211->kp_code[i]);
|
|
|
|
error = input_register_device(kp_idev);
|
|
if (error)
|
|
dev_err(&client->dev, "Failed to register %s: %d\n",
|
|
kp_idev->name, error);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int iqs7211_register_tp(struct iqs7211_private *iqs7211)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct touchscreen_properties *prop = &iqs7211->prop;
|
|
struct input_dev *tp_idev = iqs7211->tp_idev;
|
|
struct i2c_client *client = iqs7211->client;
|
|
int error;
|
|
|
|
error = device_property_read_u32(&client->dev, "azoteq,num-contacts",
|
|
&iqs7211->num_contacts);
|
|
if (error == -EINVAL) {
|
|
return 0;
|
|
} else if (error) {
|
|
dev_err(&client->dev, "Failed to read number of contacts: %d\n",
|
|
error);
|
|
return error;
|
|
} else if (iqs7211->num_contacts > IQS7211_MAX_CONTACTS) {
|
|
dev_err(&client->dev, "Invalid number of contacts: %u\n",
|
|
iqs7211->num_contacts);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iqs7211->tp_config.num_contacts = iqs7211->num_contacts ? : 1;
|
|
|
|
if (!iqs7211->num_contacts)
|
|
return 0;
|
|
|
|
iqs7211->event_mask |= IQS7211_EVENT_MASK_MOVE;
|
|
|
|
tp_idev = devm_input_allocate_device(&client->dev);
|
|
if (!tp_idev)
|
|
return -ENOMEM;
|
|
|
|
iqs7211->tp_idev = tp_idev;
|
|
|
|
tp_idev->name = dev_desc->tp_name;
|
|
tp_idev->id.bustype = BUS_I2C;
|
|
|
|
input_set_abs_params(tp_idev, ABS_MT_POSITION_X,
|
|
0, le16_to_cpu(iqs7211->tp_config.max_x), 0, 0);
|
|
|
|
input_set_abs_params(tp_idev, ABS_MT_POSITION_Y,
|
|
0, le16_to_cpu(iqs7211->tp_config.max_y), 0, 0);
|
|
|
|
input_set_abs_params(tp_idev, ABS_MT_PRESSURE, 0, U16_MAX, 0, 0);
|
|
|
|
touchscreen_parse_properties(tp_idev, true, prop);
|
|
|
|
/*
|
|
* The device reserves 0xFFFF for coordinates that correspond to slots
|
|
* which are not in a state of touch.
|
|
*/
|
|
if (prop->max_x >= U16_MAX || prop->max_y >= U16_MAX) {
|
|
dev_err(&client->dev, "Invalid trackpad size: %u*%u\n",
|
|
prop->max_x, prop->max_y);
|
|
return -EINVAL;
|
|
}
|
|
|
|
iqs7211->tp_config.max_x = cpu_to_le16(prop->max_x);
|
|
iqs7211->tp_config.max_y = cpu_to_le16(prop->max_y);
|
|
|
|
error = input_mt_init_slots(tp_idev, iqs7211->num_contacts,
|
|
INPUT_MT_DIRECT);
|
|
if (error) {
|
|
dev_err(&client->dev, "Failed to initialize slots: %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
error = input_register_device(tp_idev);
|
|
if (error)
|
|
dev_err(&client->dev, "Failed to register %s: %d\n",
|
|
tp_idev->name, error);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int iqs7211_report(struct iqs7211_private *iqs7211)
|
|
{
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
struct i2c_client *client = iqs7211->client;
|
|
struct iqs7211_touch_data *touch_data;
|
|
u16 info_flags, charge_mode, gesture_flags;
|
|
__le16 status[12];
|
|
int error, i;
|
|
|
|
error = iqs7211_read_burst(iqs7211, dev_desc->sys_stat, status,
|
|
dev_desc->contact_offs * sizeof(__le16) +
|
|
iqs7211->num_contacts * sizeof(*touch_data));
|
|
if (error)
|
|
return error;
|
|
|
|
info_flags = le16_to_cpu(status[dev_desc->info_offs]);
|
|
|
|
if (info_flags & dev_desc->show_reset) {
|
|
dev_err(&client->dev, "Unexpected device reset\n");
|
|
|
|
/*
|
|
* The device may or may not expect forced communication after
|
|
* it exits hardware reset, so the corresponding state machine
|
|
* must be reset as well.
|
|
*/
|
|
iqs7211->comms_mode = iqs7211->comms_init;
|
|
|
|
return iqs7211_init_device(iqs7211);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dev_desc->ati_error); i++) {
|
|
if (!(info_flags & dev_desc->ati_error[i]))
|
|
continue;
|
|
|
|
dev_err(&client->dev, "Unexpected %s ATI error\n",
|
|
iqs7211_reg_grp_names[i]);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < iqs7211->num_contacts; i++) {
|
|
u16 pressure;
|
|
|
|
touch_data = (struct iqs7211_touch_data *)
|
|
&status[dev_desc->contact_offs] + i;
|
|
pressure = le16_to_cpu(touch_data->pressure);
|
|
|
|
input_mt_slot(iqs7211->tp_idev, i);
|
|
if (input_mt_report_slot_state(iqs7211->tp_idev, MT_TOOL_FINGER,
|
|
pressure != 0)) {
|
|
touchscreen_report_pos(iqs7211->tp_idev, &iqs7211->prop,
|
|
le16_to_cpu(touch_data->abs_x),
|
|
le16_to_cpu(touch_data->abs_y),
|
|
true);
|
|
input_report_abs(iqs7211->tp_idev, ABS_MT_PRESSURE,
|
|
pressure);
|
|
}
|
|
}
|
|
|
|
if (iqs7211->num_contacts) {
|
|
input_mt_sync_frame(iqs7211->tp_idev);
|
|
input_sync(iqs7211->tp_idev);
|
|
}
|
|
|
|
if (!iqs7211->kp_idev)
|
|
return 0;
|
|
|
|
charge_mode = info_flags & GENMASK(dev_desc->charge_shift + 2,
|
|
dev_desc->charge_shift);
|
|
charge_mode >>= dev_desc->charge_shift;
|
|
|
|
/*
|
|
* A charging mode higher than 2 (idle mode) indicates the device last
|
|
* operated in low-power mode and intends to express an ALP event.
|
|
*/
|
|
if (info_flags & dev_desc->kp_events->mask && charge_mode > 2) {
|
|
input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 1);
|
|
input_sync(iqs7211->kp_idev);
|
|
|
|
input_report_key(iqs7211->kp_idev, *iqs7211->kp_code, 0);
|
|
}
|
|
|
|
for (i = 0; i < dev_desc->num_kp_events; i++) {
|
|
if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_BTN)
|
|
continue;
|
|
|
|
input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i],
|
|
info_flags & dev_desc->kp_events[i].mask);
|
|
}
|
|
|
|
gesture_flags = le16_to_cpu(status[dev_desc->gesture_offs]);
|
|
|
|
for (i = 0; i < dev_desc->num_kp_events; i++) {
|
|
enum iqs7211_reg_key_id reg_key = dev_desc->kp_events[i].reg_key;
|
|
u16 mask = dev_desc->kp_events[i].mask;
|
|
|
|
if (dev_desc->kp_events[i].reg_grp != IQS7211_REG_GRP_TP)
|
|
continue;
|
|
|
|
if ((gesture_flags ^ iqs7211->gesture_cache) & mask)
|
|
input_report_key(iqs7211->kp_idev, iqs7211->kp_code[i],
|
|
gesture_flags & mask);
|
|
|
|
iqs7211->gesture_cache &= ~mask;
|
|
|
|
/*
|
|
* Hold and palm gestures persist while the contact remains in
|
|
* place; all others are momentary and hence are followed by a
|
|
* complementary release event.
|
|
*/
|
|
if (reg_key == IQS7211_REG_KEY_HOLD ||
|
|
reg_key == IQS7211_REG_KEY_PALM) {
|
|
iqs7211->gesture_cache |= gesture_flags & mask;
|
|
gesture_flags &= ~mask;
|
|
}
|
|
}
|
|
|
|
if (gesture_flags) {
|
|
input_sync(iqs7211->kp_idev);
|
|
|
|
for (i = 0; i < dev_desc->num_kp_events; i++)
|
|
if (dev_desc->kp_events[i].reg_grp == IQS7211_REG_GRP_TP &&
|
|
gesture_flags & dev_desc->kp_events[i].mask)
|
|
input_report_key(iqs7211->kp_idev,
|
|
iqs7211->kp_code[i], 0);
|
|
}
|
|
|
|
input_sync(iqs7211->kp_idev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t iqs7211_irq(int irq, void *context)
|
|
{
|
|
struct iqs7211_private *iqs7211 = context;
|
|
|
|
return iqs7211_report(iqs7211) ? IRQ_NONE : IRQ_HANDLED;
|
|
}
|
|
|
|
static int iqs7211_suspend(struct device *dev)
|
|
{
|
|
struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
int error;
|
|
|
|
if (!dev_desc->suspend || device_may_wakeup(dev))
|
|
return 0;
|
|
|
|
/*
|
|
* I2C communication prompts the device to assert its RDY pin if it is
|
|
* not already asserted. As such, the interrupt must be disabled so as
|
|
* to prevent reentrant interrupts.
|
|
*/
|
|
disable_irq(gpiod_to_irq(iqs7211->irq_gpio));
|
|
|
|
error = iqs7211_write_word(iqs7211, dev_desc->sys_ctrl,
|
|
dev_desc->suspend);
|
|
|
|
enable_irq(gpiod_to_irq(iqs7211->irq_gpio));
|
|
|
|
return error;
|
|
}
|
|
|
|
static int iqs7211_resume(struct device *dev)
|
|
{
|
|
struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
|
|
const struct iqs7211_dev_desc *dev_desc = iqs7211->dev_desc;
|
|
__le16 sys_ctrl[] = {
|
|
0,
|
|
cpu_to_le16(iqs7211->event_mask),
|
|
};
|
|
int error;
|
|
|
|
if (!dev_desc->suspend || device_may_wakeup(dev))
|
|
return 0;
|
|
|
|
disable_irq(gpiod_to_irq(iqs7211->irq_gpio));
|
|
|
|
/*
|
|
* Forced communication, if in use, must be explicitly enabled as part
|
|
* of the wake-up command.
|
|
*/
|
|
error = iqs7211_write_burst(iqs7211, dev_desc->sys_ctrl, sys_ctrl,
|
|
sizeof(sys_ctrl));
|
|
|
|
enable_irq(gpiod_to_irq(iqs7211->irq_gpio));
|
|
|
|
return error;
|
|
}
|
|
|
|
static DEFINE_SIMPLE_DEV_PM_OPS(iqs7211_pm, iqs7211_suspend, iqs7211_resume);
|
|
|
|
static ssize_t fw_info_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct iqs7211_private *iqs7211 = dev_get_drvdata(dev);
|
|
|
|
return sysfs_emit(buf, "%u.%u.%u.%u:%u.%u\n",
|
|
le16_to_cpu(iqs7211->ver_info.prod_num),
|
|
le32_to_cpu(iqs7211->ver_info.patch),
|
|
le16_to_cpu(iqs7211->ver_info.major),
|
|
le16_to_cpu(iqs7211->ver_info.minor),
|
|
iqs7211->exp_file[1], iqs7211->exp_file[0]);
|
|
}
|
|
|
|
static DEVICE_ATTR_RO(fw_info);
|
|
|
|
static struct attribute *iqs7211_attrs[] = {
|
|
&dev_attr_fw_info.attr,
|
|
NULL
|
|
};
|
|
ATTRIBUTE_GROUPS(iqs7211);
|
|
|
|
static const struct of_device_id iqs7211_of_match[] = {
|
|
{
|
|
.compatible = "azoteq,iqs7210a",
|
|
.data = &iqs7211_devs[IQS7210A],
|
|
},
|
|
{
|
|
.compatible = "azoteq,iqs7211a",
|
|
.data = &iqs7211_devs[IQS7211A],
|
|
},
|
|
{
|
|
.compatible = "azoteq,iqs7211e",
|
|
.data = &iqs7211_devs[IQS7211E],
|
|
},
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, iqs7211_of_match);
|
|
|
|
static int iqs7211_probe(struct i2c_client *client)
|
|
{
|
|
struct iqs7211_private *iqs7211;
|
|
enum iqs7211_reg_grp_id reg_grp;
|
|
unsigned long irq_flags;
|
|
bool shared_irq;
|
|
int error, irq;
|
|
|
|
iqs7211 = devm_kzalloc(&client->dev, sizeof(*iqs7211), GFP_KERNEL);
|
|
if (!iqs7211)
|
|
return -ENOMEM;
|
|
|
|
i2c_set_clientdata(client, iqs7211);
|
|
iqs7211->client = client;
|
|
|
|
INIT_LIST_HEAD(&iqs7211->reg_field_head);
|
|
|
|
iqs7211->dev_desc = device_get_match_data(&client->dev);
|
|
if (!iqs7211->dev_desc)
|
|
return -ENODEV;
|
|
|
|
shared_irq = iqs7211->dev_desc->num_ctx == IQS7211_MAX_CTX;
|
|
|
|
/*
|
|
* The RDY pin behaves as an interrupt, but must also be polled ahead
|
|
* of unsolicited I2C communication. As such, it is first opened as a
|
|
* GPIO and then passed to gpiod_to_irq() to register the interrupt.
|
|
*
|
|
* If an extra CTx pin is present, the RDY and MCLR pins are combined
|
|
* into a single bidirectional pin. In that case, the platform's GPIO
|
|
* must be configured as an open-drain output.
|
|
*/
|
|
iqs7211->irq_gpio = devm_gpiod_get(&client->dev, "irq",
|
|
shared_irq ? GPIOD_OUT_LOW
|
|
: GPIOD_IN);
|
|
if (IS_ERR(iqs7211->irq_gpio)) {
|
|
error = PTR_ERR(iqs7211->irq_gpio);
|
|
dev_err(&client->dev, "Failed to request IRQ GPIO: %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
if (shared_irq) {
|
|
iqs7211->reset_gpio = iqs7211->irq_gpio;
|
|
} else {
|
|
iqs7211->reset_gpio = devm_gpiod_get_optional(&client->dev,
|
|
"reset",
|
|
GPIOD_OUT_HIGH);
|
|
if (IS_ERR(iqs7211->reset_gpio)) {
|
|
error = PTR_ERR(iqs7211->reset_gpio);
|
|
dev_err(&client->dev,
|
|
"Failed to request reset GPIO: %d\n", error);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
error = iqs7211_start_comms(iqs7211);
|
|
if (error)
|
|
return error;
|
|
|
|
for (reg_grp = 0; reg_grp < IQS7211_NUM_REG_GRPS; reg_grp++) {
|
|
const char *reg_grp_name = iqs7211_reg_grp_names[reg_grp];
|
|
struct fwnode_handle *reg_grp_node;
|
|
|
|
if (reg_grp_name)
|
|
reg_grp_node = device_get_named_child_node(&client->dev,
|
|
reg_grp_name);
|
|
else
|
|
reg_grp_node = fwnode_handle_get(dev_fwnode(&client->dev));
|
|
|
|
if (!reg_grp_node)
|
|
continue;
|
|
|
|
error = iqs7211_parse_reg_grp(iqs7211, reg_grp_node, reg_grp);
|
|
fwnode_handle_put(reg_grp_node);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
error = iqs7211_register_kp(iqs7211);
|
|
if (error)
|
|
return error;
|
|
|
|
error = iqs7211_register_tp(iqs7211);
|
|
if (error)
|
|
return error;
|
|
|
|
error = iqs7211_init_device(iqs7211);
|
|
if (error)
|
|
return error;
|
|
|
|
irq = gpiod_to_irq(iqs7211->irq_gpio);
|
|
if (irq < 0)
|
|
return irq;
|
|
|
|
irq_flags = gpiod_is_active_low(iqs7211->irq_gpio) ? IRQF_TRIGGER_LOW
|
|
: IRQF_TRIGGER_HIGH;
|
|
irq_flags |= IRQF_ONESHOT;
|
|
|
|
error = devm_request_threaded_irq(&client->dev, irq, NULL, iqs7211_irq,
|
|
irq_flags, client->name, iqs7211);
|
|
if (error)
|
|
dev_err(&client->dev, "Failed to request IRQ: %d\n", error);
|
|
|
|
return error;
|
|
}
|
|
|
|
static struct i2c_driver iqs7211_i2c_driver = {
|
|
.probe = iqs7211_probe,
|
|
.driver = {
|
|
.name = "iqs7211",
|
|
.of_match_table = iqs7211_of_match,
|
|
.dev_groups = iqs7211_groups,
|
|
.pm = pm_sleep_ptr(&iqs7211_pm),
|
|
},
|
|
};
|
|
module_i2c_driver(iqs7211_i2c_driver);
|
|
|
|
MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
|
|
MODULE_DESCRIPTION("Azoteq IQS7210A/7211A/E Trackpad/Touchscreen Controller");
|
|
MODULE_LICENSE("GPL");
|