Release 4.11 drivers/input/touchscreen/melfas_mip4.c
/*
* MELFAS MIP4 Touchscreen
*
* Copyright (C) 2016 MELFAS Inc.
*
* Author : Sangwon Jee <jeesw@melfas.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#define MIP4_DEVICE_NAME "mip4_ts"
/*****************************************************************
* Protocol
* Version : MIP 4.0 Rev 5.4
*****************************************************************/
/* Address */
#define MIP4_R0_BOOT 0x00
#define MIP4_R1_BOOT_MODE 0x01
#define MIP4_R1_BOOT_BUF_ADDR 0x10
#define MIP4_R1_BOOT_STATUS 0x20
#define MIP4_R1_BOOT_CMD 0x30
#define MIP4_R1_BOOT_TARGET_ADDR 0x40
#define MIP4_R1_BOOT_SIZE 0x44
#define MIP4_R0_INFO 0x01
#define MIP4_R1_INFO_PRODUCT_NAME 0x00
#define MIP4_R1_INFO_RESOLUTION_X 0x10
#define MIP4_R1_INFO_RESOLUTION_Y 0x12
#define MIP4_R1_INFO_NODE_NUM_X 0x14
#define MIP4_R1_INFO_NODE_NUM_Y 0x15
#define MIP4_R1_INFO_KEY_NUM 0x16
#define MIP4_R1_INFO_PRESSURE_NUM 0x17
#define MIP4_R1_INFO_LENGTH_X 0x18
#define MIP4_R1_INFO_LENGTH_Y 0x1A
#define MIP4_R1_INFO_PPM_X 0x1C
#define MIP4_R1_INFO_PPM_Y 0x1D
#define MIP4_R1_INFO_VERSION_BOOT 0x20
#define MIP4_R1_INFO_VERSION_CORE 0x22
#define MIP4_R1_INFO_VERSION_APP 0x24
#define MIP4_R1_INFO_VERSION_PARAM 0x26
#define MIP4_R1_INFO_SECT_BOOT_START 0x30
#define MIP4_R1_INFO_SECT_BOOT_END 0x31
#define MIP4_R1_INFO_SECT_CORE_START 0x32
#define MIP4_R1_INFO_SECT_CORE_END 0x33
#define MIP4_R1_INFO_SECT_APP_START 0x34
#define MIP4_R1_INFO_SECT_APP_END 0x35
#define MIP4_R1_INFO_SECT_PARAM_START 0x36
#define MIP4_R1_INFO_SECT_PARAM_END 0x37
#define MIP4_R1_INFO_BUILD_DATE 0x40
#define MIP4_R1_INFO_BUILD_TIME 0x44
#define MIP4_R1_INFO_CHECKSUM_PRECALC 0x48
#define MIP4_R1_INFO_CHECKSUM_REALTIME 0x4A
#define MIP4_R1_INFO_PROTOCOL_NAME 0x50
#define MIP4_R1_INFO_PROTOCOL_VERSION 0x58
#define MIP4_R1_INFO_IC_ID 0x70
#define MIP4_R1_INFO_IC_NAME 0x71
#define MIP4_R1_INFO_IC_VENDOR_ID 0x75
#define MIP4_R1_INFO_IC_HW_CATEGORY 0x77
#define MIP4_R1_INFO_CONTACT_THD_SCR 0x78
#define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A
#define MIP4_R1_INFO_PID 0x7C
#define MIP4_R1_INFO_VID 0x7E
#define MIP4_R1_INFO_SLAVE_ADDR 0x80
#define MIP4_R0_EVENT 0x02
#define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00
#define MIP4_R1_EVENT_FORMAT 0x04
#define MIP4_R1_EVENT_SIZE 0x06
#define MIP4_R1_EVENT_PACKET_INFO 0x10
#define MIP4_R1_EVENT_PACKET_DATA 0x11
#define MIP4_R0_CTRL 0x06
#define MIP4_R1_CTRL_READY_STATUS 0x00
#define MIP4_R1_CTRL_EVENT_READY 0x01
#define MIP4_R1_CTRL_MODE 0x10
#define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE 0x11
#define MIP4_R1_CTRL_RECALIBRATE 0x12
#define MIP4_R1_CTRL_POWER_STATE 0x13
#define MIP4_R1_CTRL_GESTURE_TYPE 0x14
#define MIP4_R1_CTRL_DISABLE_ESD_ALERT 0x18
#define MIP4_R1_CTRL_CHARGER_MODE 0x19
#define MIP4_R1_CTRL_HIGH_SENS_MODE 0x1A
#define MIP4_R1_CTRL_WINDOW_MODE 0x1B
#define MIP4_R1_CTRL_PALM_REJECTION 0x1C
#define MIP4_R1_CTRL_EDGE_CORRECTION 0x1D
#define MIP4_R1_CTRL_ENTER_GLOVE_MODE 0x1E
#define MIP4_R1_CTRL_I2C_ON_LPM 0x1F
#define MIP4_R1_CTRL_GESTURE_DEBUG 0x20
#define MIP4_R1_CTRL_PALM_EVENT 0x22
#define MIP4_R1_CTRL_PROXIMITY_SENSING 0x23
/* Value */
#define MIP4_BOOT_MODE_BOOT 0x01
#define MIP4_BOOT_MODE_APP 0x02
#define MIP4_BOOT_STATUS_BUSY 0x05
#define MIP4_BOOT_STATUS_ERROR 0x0E
#define MIP4_BOOT_STATUS_DONE 0xA0
#define MIP4_BOOT_CMD_MASS_ERASE 0x15
#define MIP4_BOOT_CMD_PROGRAM 0x54
#define MIP4_BOOT_CMD_ERASE 0x8F
#define MIP4_BOOT_CMD_WRITE 0xA5
#define MIP4_BOOT_CMD_READ 0xC2
#define MIP4_EVENT_INPUT_TYPE_KEY 0
#define MIP4_EVENT_INPUT_TYPE_SCREEN 1
#define MIP4_EVENT_INPUT_TYPE_PROXIMITY 2
#define I2C_RETRY_COUNT 3
/* 2~ */
#define MIP4_BUF_SIZE 128
#define MIP4_MAX_FINGERS 10
#define MIP4_MAX_KEYS 4
#define MIP4_TOUCH_MAJOR_MIN 0
#define MIP4_TOUCH_MAJOR_MAX 255
#define MIP4_TOUCH_MINOR_MIN 0
#define MIP4_TOUCH_MINOR_MAX 255
#define MIP4_PRESSURE_MIN 0
#define MIP4_PRESSURE_MAX 255
#define MIP4_FW_NAME "melfas_mip4.fw"
#define MIP4_FW_UPDATE_DEBUG 0
/* 0 (default) or 1 */
struct mip4_fw_version {
u16 boot;
u16 core;
u16 app;
u16 param;
};
struct mip4_ts {
struct i2c_client *client;
struct input_dev *input;
struct gpio_desc *gpio_ce;
char phys[32];
char product_name[16];
u16 product_id;
char ic_name[4];
char fw_name[32];
unsigned int max_x;
unsigned int max_y;
u8 node_x;
u8 node_y;
u8 node_key;
unsigned int ppm_x;
unsigned int ppm_y;
struct mip4_fw_version fw_version;
unsigned int event_size;
unsigned int event_format;
unsigned int key_num;
unsigned short key_code[MIP4_MAX_KEYS];
bool wake_irq_enabled;
u8 buf[MIP4_BUF_SIZE];
};
static int mip4_i2c_xfer(struct mip4_ts *ts,
char *write_buf, unsigned int write_len,
char *read_buf, unsigned int read_len)
{
struct i2c_msg msg[] = {
{
.addr = ts->client->addr,
.flags = 0,
.buf = write_buf,
.len = write_len,
}, {
.addr = ts->client->addr,
.flags = I2C_M_RD,
.buf = read_buf,
.len = read_len,
},
};
int retry = I2C_RETRY_COUNT;
int res;
int error;
do {
res = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
if (res == ARRAY_SIZE(msg))
return 0;
error = res < 0 ? res : -EIO;
dev_err(&ts->client->dev,
"%s - i2c_transfer failed: %d (%d)\n",
__func__, error, res);
} while (--retry);
return error;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 168 | 100.00% | 1 | 100.00% |
Total | 168 | 100.00% | 1 | 100.00% |
static void mip4_parse_fw_version(const u8 *buf, struct mip4_fw_version *v)
{
v->boot = get_unaligned_le16(buf + 0);
v->core = get_unaligned_le16(buf + 2);
v->app = get_unaligned_le16(buf + 4);
v->param = get_unaligned_le16(buf + 6);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 60 | 100.00% | 1 | 100.00% |
Total | 60 | 100.00% | 1 | 100.00% |
/*
* Read chip firmware version
*/
static int mip4_get_fw_version(struct mip4_ts *ts)
{
u8 cmd[] = { MIP4_R0_INFO, MIP4_R1_INFO_VERSION_BOOT };
u8 buf[sizeof(ts->fw_version)];
int error;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, sizeof(buf));
if (error) {
memset(&ts->fw_version, 0xff, sizeof(ts->fw_version));
return error;
}
mip4_parse_fw_version(buf, &ts->fw_version);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 95 | 100.00% | 1 | 100.00% |
Total | 95 | 100.00% | 1 | 100.00% |
/*
* Fetch device characteristics
*/
static int mip4_query_device(struct mip4_ts *ts)
{
int error;
u8 cmd[2];
u8 buf[14];
/* Product name */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_PRODUCT_NAME;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
ts->product_name, sizeof(ts->product_name));
if (error)
dev_warn(&ts->client->dev,
"Failed to retrieve product name: %d\n", error);
else
dev_dbg(&ts->client->dev, "product name: %.*s\n",
(int)sizeof(ts->product_name), ts->product_name);
/* Product ID */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_PID;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 2);
if (error) {
dev_warn(&ts->client->dev,
"Failed to retrieve product id: %d\n", error);
} else {
ts->product_id = get_unaligned_le16(&buf[0]);
dev_dbg(&ts->client->dev, "product id: %04X\n", ts->product_id);
}
/* Firmware name */
snprintf(ts->fw_name, sizeof(ts->fw_name),
"melfas_mip4_%04X.fw", ts->product_id);
dev_dbg(&ts->client->dev, "firmware name: %s\n", ts->fw_name);
/* IC name */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_IC_NAME;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
ts->ic_name, sizeof(ts->ic_name));
if (error)
dev_warn(&ts->client->dev,
"Failed to retrieve IC name: %d\n", error);
else
dev_dbg(&ts->client->dev, "IC name: %.*s\n",
(int)sizeof(ts->ic_name), ts->ic_name);
/* Firmware version */
error = mip4_get_fw_version(ts);
if (error)
dev_warn(&ts->client->dev,
"Failed to retrieve FW version: %d\n", error);
else
dev_dbg(&ts->client->dev, "F/W Version: %04X %04X %04X %04X\n",
ts->fw_version.boot, ts->fw_version.core,
ts->fw_version.app, ts->fw_version.param);
/* Resolution */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_RESOLUTION_X;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 14);
if (error) {
dev_warn(&ts->client->dev,
"Failed to retrieve touchscreen parameters: %d\n",
error);
} else {
ts->max_x = get_unaligned_le16(&buf[0]);
ts->max_y = get_unaligned_le16(&buf[2]);
dev_dbg(&ts->client->dev, "max_x: %d, max_y: %d\n",
ts->max_x, ts->max_y);
ts->node_x = buf[4];
ts->node_y = buf[5];
ts->node_key = buf[6];
dev_dbg(&ts->client->dev,
"node_x: %d, node_y: %d, node_key: %d\n",
ts->node_x, ts->node_y, ts->node_key);
ts->ppm_x = buf[12];
ts->ppm_y = buf[13];
dev_dbg(&ts->client->dev, "ppm_x: %d, ppm_y: %d\n",
ts->ppm_x, ts->ppm_y);
/* Key ts */
if (ts->node_key > 0)
ts->key_num = ts->node_key;
}
/* Protocol */
cmd[0] = MIP4_R0_EVENT;
cmd[1] = MIP4_R1_EVENT_SUPPORTED_FUNC;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7);
if (error) {
dev_warn(&ts->client->dev,
"Failed to retrieve device type: %d\n", error);
ts->event_format = 0xff;
} else {
ts->event_format = get_unaligned_le16(&buf[4]);
ts->event_size = buf[6];
dev_dbg(&ts->client->dev, "event_format: %d, event_size: %d\n",
ts->event_format, ts->event_size);
if (ts->event_format == 2 || ts->event_format > 3)
dev_warn(&ts->client->dev,
"Unknown event format %d\n", ts->event_format);
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 726 | 100.00% | 5 | 100.00% |
Total | 726 | 100.00% | 5 | 100.00% |
static int mip4_power_on(struct mip4_ts *ts)
{
if (ts->gpio_ce) {
gpiod_set_value_cansleep(ts->gpio_ce, 1);
/* Booting delay : 200~300ms */
usleep_range(200 * 1000, 300 * 1000);
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 43 | 100.00% | 1 | 100.00% |
Total | 43 | 100.00% | 1 | 100.00% |
static void mip4_power_off(struct mip4_ts *ts)
{
if (ts->gpio_ce)
gpiod_set_value_cansleep(ts->gpio_ce, 0);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 26 | 100.00% | 1 | 100.00% |
Total | 26 | 100.00% | 1 | 100.00% |
/*
* Clear touch input event status
*/
static void mip4_clear_input(struct mip4_ts *ts)
{
int i;
/* Screen */
for (i = 0; i < MIP4_MAX_FINGERS; i++) {
input_mt_slot(ts->input, i);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
}
/* Keys */
for (i = 0; i < ts->key_num; i++)
input_report_key(ts->input, ts->key_code[i], 0);
input_sync(ts->input);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 89 | 100.00% | 1 | 100.00% |
Total | 89 | 100.00% | 1 | 100.00% |
static int mip4_enable(struct mip4_ts *ts)
{
int error;
error = mip4_power_on(ts);
if (error)
return error;
enable_irq(ts->client->irq);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 40 | 100.00% | 1 | 100.00% |
Total | 40 | 100.00% | 1 | 100.00% |
static void mip4_disable(struct mip4_ts *ts)
{
disable_irq(ts->client->irq);
mip4_power_off(ts);
mip4_clear_input(ts);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 30 | 100.00% | 1 | 100.00% |
Total | 30 | 100.00% | 1 | 100.00% |
/*****************************************************************
* Input handling
*****************************************************************/
static void mip4_report_keys(struct mip4_ts *ts, u8 *packet)
{
u8 key;
bool down;
switch (ts->event_format) {
case 0:
case 1:
key = packet[0] & 0x0F;
down = packet[0] & 0x80;
break;
case 3:
default:
key = packet[0] & 0x0F;
down = packet[1] & 0x01;
break;
}
/* Report key event */
if (key >= 1 && key <= ts->key_num) {
unsigned short keycode = ts->key_code[key - 1];
dev_dbg(&ts->client->dev,
"Key - ID: %d, keycode: %d, state: %d\n",
key, keycode, down);
input_event(ts->input, EV_MSC, MSC_SCAN, keycode);
input_report_key(ts->input, keycode, down);
} else {
dev_err(&ts->client->dev, "Unknown key: %d\n", key);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 164 | 100.00% | 1 | 100.00% |
Total | 164 | 100.00% | 1 | 100.00% |
static void mip4_report_touch(struct mip4_ts *ts, u8 *packet)
{
int id;
bool hover;
bool palm;
bool state;
u16 x, y;
u8 pressure_stage = 0;
u8 pressure;
u8 size;
u8 touch_major;
u8 touch_minor;
switch (ts->event_format) {
case 0:
case 1:
/* Touch only */
state = packet[0] & BIT(7);
hover = packet[0] & BIT(5);
palm = packet[0] & BIT(4);
id = (packet[0] & 0x0F) - 1;
x = ((packet[1] & 0x0F) << 8) | packet[2];
y = (((packet[1] >> 4) & 0x0F) << 8) |
packet[3];
pressure = packet[4];
size = packet[5];
if (ts->event_format == 0) {
touch_major = packet[5];
touch_minor = packet[5];
} else {
touch_major = packet[6];
touch_minor = packet[7];
}
break;
case 3:
default:
/* Touch + Force(Pressure) */
id = (packet[0] & 0x0F) - 1;
hover = packet[1] & BIT(2);
palm = packet[1] & BIT(1);
state = packet[1] & BIT(0);
x = ((packet[2] & 0x0F) << 8) | packet[3];
y = (((packet[2] >> 4) & 0x0F) << 8) |
packet[4];
size = packet[6];
pressure_stage = (packet[7] & 0xF0) >> 4;
pressure = ((packet[7] & 0x0F) << 8) |
packet[8];
touch_major = packet[9];
touch_minor = packet[10];
break;
}
dev_dbg(&ts->client->dev,
"Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d\n",
id, state, x, y, pressure);
if (unlikely(id < 0 || id >= MIP4_MAX_FINGERS)) {
dev_err(&ts->client->dev, "Screen - invalid slot ID: %d\n", id);
} else if (state) {
/* Press or Move event */
input_mt_slot(ts->input, id);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
input_report_abs(ts->input, ABS_MT_POSITION_X, x);
input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, touch_major);
input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, touch_minor);
} else {
/* Release event */
input_mt_slot(ts->input, id);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
}
input_mt_sync_frame(ts->input);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 531 | 100.00% | 1 | 100.00% |
Total | 531 | 100.00% | 1 | 100.00% |
static int mip4_handle_packet(struct mip4_ts *ts, u8 *packet)
{
u8 type;
switch (ts->event_format) {
case 0:
case 1:
type = (packet[0] & 0x40) >> 6;
break;
case 3:
type = (packet[0] & 0xF0) >> 4;
break;
default:
/* Should not happen unless we have corrupted firmware */
return -EINVAL;
}
dev_dbg(&ts->client->dev, "Type: %d\n", type);
/* Report input event */
switch (type) {
case MIP4_EVENT_INPUT_TYPE_KEY:
mip4_report_keys(ts, packet);
break;
case MIP4_EVENT_INPUT_TYPE_SCREEN:
mip4_report_touch(ts, packet);
break;
default:
dev_err(&ts->client->dev, "Unknown event type: %d\n", type);
break;
}
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 131 | 100.00% | 1 | 100.00% |
Total | 131 | 100.00% | 1 | 100.00% |
static irqreturn_t mip4_interrupt(int irq, void *dev_id)
{
struct mip4_ts *ts = dev_id;
struct i2c_client *client = ts->client;
unsigned int i;
int error;
u8 cmd[2];
u8 size;
bool alert;
/* Read packet info */
cmd[0] = MIP4_R0_EVENT;
cmd[1] = MIP4_R1_EVENT_PACKET_INFO;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, 1);
if (error) {
dev_err(&client->dev,
"Failed to read packet info: %d\n", error);
goto out;
}
size = ts->buf[0] & 0x7F;
alert = ts->buf[0] & BIT(7);
dev_dbg(&client->dev, "packet size: %d, alert: %d\n", size, alert);
/* Check size */
if (!size) {
dev_err(&client->dev, "Empty packet\n");
goto out;
}
/* Read packet data */
cmd[0] = MIP4_R0_EVENT;
cmd[1] = MIP4_R1_EVENT_PACKET_DATA;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, size);
if (error) {
dev_err(&client->dev,
"Failed to read packet data: %d\n", error);
goto out;
}
if (alert) {
dev_dbg(&client->dev, "Alert: %d\n", ts->buf[0]);
} else {
for (i = 0; i < size; i += ts->event_size) {
error = mip4_handle_packet(ts, &ts->buf[i]);
if (error)
break;
}
input_sync(ts->input);
}
out:
return IRQ_HANDLED;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 296 | 100.00% | 1 | 100.00% |
Total | 296 | 100.00% | 1 | 100.00% |
static int mip4_input_open(struct input_dev *dev)
{
struct mip4_ts *ts = input_get_drvdata(dev);
return mip4_enable(ts);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 27 | 100.00% | 1 | 100.00% |
Total | 27 | 100.00% | 1 | 100.00% |
static void mip4_input_close(struct input_dev *dev)
{
struct mip4_ts *ts = input_get_drvdata(dev);
mip4_disable(ts);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Sangwon Jee | 26 | 100.00% | 1 | 100.00% |
Total | 26 | 100.00% | 1 | 100.00% |
/*****************************************************************
* Firmware update
*****************************************************************/
/* Firmware Info */
#define MIP4_BL_PAGE_SIZE 512
/* 512 */
#define MIP4_BL_PACKET_SIZE 512
/* 512, 256, 128, 64, ... */
/*
* Firmware binary tail info
*/