162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mcs5000_ts.c - Touchscreen driver for MELFAS MCS-5000 controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009 Samsung Electronics Co.Ltd 662306a36Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on wm97xx-core.c 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/i2c.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/input.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/platform_data/mcs.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Registers */ 2062306a36Sopenharmony_ci#define MCS5000_TS_STATUS 0x00 2162306a36Sopenharmony_ci#define STATUS_OFFSET 0 2262306a36Sopenharmony_ci#define STATUS_NO (0 << STATUS_OFFSET) 2362306a36Sopenharmony_ci#define STATUS_INIT (1 << STATUS_OFFSET) 2462306a36Sopenharmony_ci#define STATUS_SENSING (2 << STATUS_OFFSET) 2562306a36Sopenharmony_ci#define STATUS_COORD (3 << STATUS_OFFSET) 2662306a36Sopenharmony_ci#define STATUS_GESTURE (4 << STATUS_OFFSET) 2762306a36Sopenharmony_ci#define ERROR_OFFSET 4 2862306a36Sopenharmony_ci#define ERROR_NO (0 << ERROR_OFFSET) 2962306a36Sopenharmony_ci#define ERROR_POWER_ON_RESET (1 << ERROR_OFFSET) 3062306a36Sopenharmony_ci#define ERROR_INT_RESET (2 << ERROR_OFFSET) 3162306a36Sopenharmony_ci#define ERROR_EXT_RESET (3 << ERROR_OFFSET) 3262306a36Sopenharmony_ci#define ERROR_INVALID_REG_ADDRESS (8 << ERROR_OFFSET) 3362306a36Sopenharmony_ci#define ERROR_INVALID_REG_VALUE (9 << ERROR_OFFSET) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define MCS5000_TS_OP_MODE 0x01 3662306a36Sopenharmony_ci#define RESET_OFFSET 0 3762306a36Sopenharmony_ci#define RESET_NO (0 << RESET_OFFSET) 3862306a36Sopenharmony_ci#define RESET_EXT_SOFT (1 << RESET_OFFSET) 3962306a36Sopenharmony_ci#define OP_MODE_OFFSET 1 4062306a36Sopenharmony_ci#define OP_MODE_SLEEP (0 << OP_MODE_OFFSET) 4162306a36Sopenharmony_ci#define OP_MODE_ACTIVE (1 << OP_MODE_OFFSET) 4262306a36Sopenharmony_ci#define GESTURE_OFFSET 4 4362306a36Sopenharmony_ci#define GESTURE_DISABLE (0 << GESTURE_OFFSET) 4462306a36Sopenharmony_ci#define GESTURE_ENABLE (1 << GESTURE_OFFSET) 4562306a36Sopenharmony_ci#define PROXIMITY_OFFSET 5 4662306a36Sopenharmony_ci#define PROXIMITY_DISABLE (0 << PROXIMITY_OFFSET) 4762306a36Sopenharmony_ci#define PROXIMITY_ENABLE (1 << PROXIMITY_OFFSET) 4862306a36Sopenharmony_ci#define SCAN_MODE_OFFSET 6 4962306a36Sopenharmony_ci#define SCAN_MODE_INTERRUPT (0 << SCAN_MODE_OFFSET) 5062306a36Sopenharmony_ci#define SCAN_MODE_POLLING (1 << SCAN_MODE_OFFSET) 5162306a36Sopenharmony_ci#define REPORT_RATE_OFFSET 7 5262306a36Sopenharmony_ci#define REPORT_RATE_40 (0 << REPORT_RATE_OFFSET) 5362306a36Sopenharmony_ci#define REPORT_RATE_80 (1 << REPORT_RATE_OFFSET) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define MCS5000_TS_SENS_CTL 0x02 5662306a36Sopenharmony_ci#define MCS5000_TS_FILTER_CTL 0x03 5762306a36Sopenharmony_ci#define PRI_FILTER_OFFSET 0 5862306a36Sopenharmony_ci#define SEC_FILTER_OFFSET 4 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define MCS5000_TS_X_SIZE_UPPER 0x08 6162306a36Sopenharmony_ci#define MCS5000_TS_X_SIZE_LOWER 0x09 6262306a36Sopenharmony_ci#define MCS5000_TS_Y_SIZE_UPPER 0x0A 6362306a36Sopenharmony_ci#define MCS5000_TS_Y_SIZE_LOWER 0x0B 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define MCS5000_TS_INPUT_INFO 0x10 6662306a36Sopenharmony_ci#define INPUT_TYPE_OFFSET 0 6762306a36Sopenharmony_ci#define INPUT_TYPE_NONTOUCH (0 << INPUT_TYPE_OFFSET) 6862306a36Sopenharmony_ci#define INPUT_TYPE_SINGLE (1 << INPUT_TYPE_OFFSET) 6962306a36Sopenharmony_ci#define INPUT_TYPE_DUAL (2 << INPUT_TYPE_OFFSET) 7062306a36Sopenharmony_ci#define INPUT_TYPE_PALM (3 << INPUT_TYPE_OFFSET) 7162306a36Sopenharmony_ci#define INPUT_TYPE_PROXIMITY (7 << INPUT_TYPE_OFFSET) 7262306a36Sopenharmony_ci#define GESTURE_CODE_OFFSET 3 7362306a36Sopenharmony_ci#define GESTURE_CODE_NO (0 << GESTURE_CODE_OFFSET) 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define MCS5000_TS_X_POS_UPPER 0x11 7662306a36Sopenharmony_ci#define MCS5000_TS_X_POS_LOWER 0x12 7762306a36Sopenharmony_ci#define MCS5000_TS_Y_POS_UPPER 0x13 7862306a36Sopenharmony_ci#define MCS5000_TS_Y_POS_LOWER 0x14 7962306a36Sopenharmony_ci#define MCS5000_TS_Z_POS 0x15 8062306a36Sopenharmony_ci#define MCS5000_TS_WIDTH 0x16 8162306a36Sopenharmony_ci#define MCS5000_TS_GESTURE_VAL 0x17 8262306a36Sopenharmony_ci#define MCS5000_TS_MODULE_REV 0x20 8362306a36Sopenharmony_ci#define MCS5000_TS_FIRMWARE_VER 0x21 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Touchscreen absolute values */ 8662306a36Sopenharmony_ci#define MCS5000_MAX_XC 0x3ff 8762306a36Sopenharmony_ci#define MCS5000_MAX_YC 0x3ff 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cienum mcs5000_ts_read_offset { 9062306a36Sopenharmony_ci READ_INPUT_INFO, 9162306a36Sopenharmony_ci READ_X_POS_UPPER, 9262306a36Sopenharmony_ci READ_X_POS_LOWER, 9362306a36Sopenharmony_ci READ_Y_POS_UPPER, 9462306a36Sopenharmony_ci READ_Y_POS_LOWER, 9562306a36Sopenharmony_ci READ_BLOCK_SIZE, 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Each client has this additional data */ 9962306a36Sopenharmony_cistruct mcs5000_ts_data { 10062306a36Sopenharmony_ci struct i2c_client *client; 10162306a36Sopenharmony_ci struct input_dev *input_dev; 10262306a36Sopenharmony_ci const struct mcs_platform_data *platform_data; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct mcs5000_ts_data *data = dev_id; 10862306a36Sopenharmony_ci struct i2c_client *client = data->client; 10962306a36Sopenharmony_ci u8 buffer[READ_BLOCK_SIZE]; 11062306a36Sopenharmony_ci int err; 11162306a36Sopenharmony_ci int x; 11262306a36Sopenharmony_ci int y; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci err = i2c_smbus_read_i2c_block_data(client, MCS5000_TS_INPUT_INFO, 11562306a36Sopenharmony_ci READ_BLOCK_SIZE, buffer); 11662306a36Sopenharmony_ci if (err < 0) { 11762306a36Sopenharmony_ci dev_err(&client->dev, "%s, err[%d]\n", __func__, err); 11862306a36Sopenharmony_ci goto out; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci switch (buffer[READ_INPUT_INFO]) { 12262306a36Sopenharmony_ci case INPUT_TYPE_NONTOUCH: 12362306a36Sopenharmony_ci input_report_key(data->input_dev, BTN_TOUCH, 0); 12462306a36Sopenharmony_ci input_sync(data->input_dev); 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci case INPUT_TYPE_SINGLE: 12862306a36Sopenharmony_ci x = (buffer[READ_X_POS_UPPER] << 8) | buffer[READ_X_POS_LOWER]; 12962306a36Sopenharmony_ci y = (buffer[READ_Y_POS_UPPER] << 8) | buffer[READ_Y_POS_LOWER]; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci input_report_key(data->input_dev, BTN_TOUCH, 1); 13262306a36Sopenharmony_ci input_report_abs(data->input_dev, ABS_X, x); 13362306a36Sopenharmony_ci input_report_abs(data->input_dev, ABS_Y, y); 13462306a36Sopenharmony_ci input_sync(data->input_dev); 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci case INPUT_TYPE_DUAL: 13862306a36Sopenharmony_ci /* TODO */ 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci case INPUT_TYPE_PALM: 14262306a36Sopenharmony_ci /* TODO */ 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci case INPUT_TYPE_PROXIMITY: 14662306a36Sopenharmony_ci /* TODO */ 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci default: 15062306a36Sopenharmony_ci dev_err(&client->dev, "Unknown ts input type %d\n", 15162306a36Sopenharmony_ci buffer[READ_INPUT_INFO]); 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci out: 15662306a36Sopenharmony_ci return IRQ_HANDLED; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void mcs5000_ts_phys_init(struct mcs5000_ts_data *data, 16062306a36Sopenharmony_ci const struct mcs_platform_data *platform_data) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct i2c_client *client = data->client; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci /* Touch reset & sleep mode */ 16562306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, 16662306a36Sopenharmony_ci RESET_EXT_SOFT | OP_MODE_SLEEP); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Touch size */ 16962306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_UPPER, 17062306a36Sopenharmony_ci platform_data->x_size >> 8); 17162306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_X_SIZE_LOWER, 17262306a36Sopenharmony_ci platform_data->x_size & 0xff); 17362306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_UPPER, 17462306a36Sopenharmony_ci platform_data->y_size >> 8); 17562306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_Y_SIZE_LOWER, 17662306a36Sopenharmony_ci platform_data->y_size & 0xff); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* Touch active mode & 80 report rate */ 17962306a36Sopenharmony_ci i2c_smbus_write_byte_data(data->client, MCS5000_TS_OP_MODE, 18062306a36Sopenharmony_ci OP_MODE_ACTIVE | REPORT_RATE_80); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int mcs5000_ts_probe(struct i2c_client *client) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci const struct mcs_platform_data *pdata; 18662306a36Sopenharmony_ci struct mcs5000_ts_data *data; 18762306a36Sopenharmony_ci struct input_dev *input_dev; 18862306a36Sopenharmony_ci int error; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci pdata = dev_get_platdata(&client->dev); 19162306a36Sopenharmony_ci if (!pdata) 19262306a36Sopenharmony_ci return -EINVAL; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 19562306a36Sopenharmony_ci if (!data) { 19662306a36Sopenharmony_ci dev_err(&client->dev, "Failed to allocate memory\n"); 19762306a36Sopenharmony_ci return -ENOMEM; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci data->client = client; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci input_dev = devm_input_allocate_device(&client->dev); 20362306a36Sopenharmony_ci if (!input_dev) { 20462306a36Sopenharmony_ci dev_err(&client->dev, "Failed to allocate input device\n"); 20562306a36Sopenharmony_ci return -ENOMEM; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci input_dev->name = "MELFAS MCS-5000 Touchscreen"; 20962306a36Sopenharmony_ci input_dev->id.bustype = BUS_I2C; 21062306a36Sopenharmony_ci input_dev->dev.parent = &client->dev; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci __set_bit(EV_ABS, input_dev->evbit); 21362306a36Sopenharmony_ci __set_bit(EV_KEY, input_dev->evbit); 21462306a36Sopenharmony_ci __set_bit(BTN_TOUCH, input_dev->keybit); 21562306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, MCS5000_MAX_XC, 0, 0); 21662306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, MCS5000_MAX_YC, 0, 0); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci data->input_dev = input_dev; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (pdata->cfg_pin) 22162306a36Sopenharmony_ci pdata->cfg_pin(); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci error = devm_request_threaded_irq(&client->dev, client->irq, 22462306a36Sopenharmony_ci NULL, mcs5000_ts_interrupt, 22562306a36Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 22662306a36Sopenharmony_ci "mcs5000_ts", data); 22762306a36Sopenharmony_ci if (error) { 22862306a36Sopenharmony_ci dev_err(&client->dev, "Failed to register interrupt\n"); 22962306a36Sopenharmony_ci return error; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci error = input_register_device(data->input_dev); 23362306a36Sopenharmony_ci if (error) { 23462306a36Sopenharmony_ci dev_err(&client->dev, "Failed to register input device\n"); 23562306a36Sopenharmony_ci return error; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci mcs5000_ts_phys_init(data, pdata); 23962306a36Sopenharmony_ci i2c_set_clientdata(client, data); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int mcs5000_ts_suspend(struct device *dev) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Touch sleep mode */ 24962306a36Sopenharmony_ci i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int mcs5000_ts_resume(struct device *dev) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 25762306a36Sopenharmony_ci struct mcs5000_ts_data *data = i2c_get_clientdata(client); 25862306a36Sopenharmony_ci const struct mcs_platform_data *pdata = dev_get_platdata(dev); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci mcs5000_ts_phys_init(data, pdata); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, 26662306a36Sopenharmony_ci mcs5000_ts_suspend, mcs5000_ts_resume); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic const struct i2c_device_id mcs5000_ts_id[] = { 26962306a36Sopenharmony_ci { "mcs5000_ts", 0 }, 27062306a36Sopenharmony_ci { } 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mcs5000_ts_id); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic struct i2c_driver mcs5000_ts_driver = { 27562306a36Sopenharmony_ci .probe = mcs5000_ts_probe, 27662306a36Sopenharmony_ci .driver = { 27762306a36Sopenharmony_ci .name = "mcs5000_ts", 27862306a36Sopenharmony_ci .pm = pm_sleep_ptr(&mcs5000_ts_pm), 27962306a36Sopenharmony_ci }, 28062306a36Sopenharmony_ci .id_table = mcs5000_ts_id, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cimodule_i2c_driver(mcs5000_ts_driver); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* Module information */ 28662306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); 28762306a36Sopenharmony_ciMODULE_DESCRIPTION("Touchscreen driver for MELFAS MCS-5000 controller"); 28862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 289