162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/bits.h> 462306a36Sopenharmony_ci#include <linux/delay.h> 562306a36Sopenharmony_ci#include <linux/i2c.h> 662306a36Sopenharmony_ci#include <linux/input.h> 762306a36Sopenharmony_ci#include <linux/input/mt.h> 862306a36Sopenharmony_ci#include <linux/input/touchscreen.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/property.h> 1262306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define IST3038C_HIB_ACCESS (0x800B << 16) 1562306a36Sopenharmony_ci#define IST3038C_DIRECT_ACCESS BIT(31) 1662306a36Sopenharmony_ci#define IST3038C_REG_CHIPID 0x40001000 1762306a36Sopenharmony_ci#define IST3038C_REG_HIB_BASE 0x30000100 1862306a36Sopenharmony_ci#define IST3038C_REG_TOUCH_STATUS (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS) 1962306a36Sopenharmony_ci#define IST3038C_REG_TOUCH_COORD (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x8) 2062306a36Sopenharmony_ci#define IST3038C_REG_INTR_MESSAGE (IST3038C_REG_HIB_BASE | IST3038C_HIB_ACCESS | 0x4) 2162306a36Sopenharmony_ci#define IST3038C_WHOAMI 0x38c 2262306a36Sopenharmony_ci#define IST3038C_CHIP_ON_DELAY_MS 60 2362306a36Sopenharmony_ci#define IST3038C_I2C_RETRY_COUNT 3 2462306a36Sopenharmony_ci#define IST3038C_MAX_FINGER_NUM 10 2562306a36Sopenharmony_ci#define IST3038C_X_MASK GENMASK(23, 12) 2662306a36Sopenharmony_ci#define IST3038C_X_SHIFT 12 2762306a36Sopenharmony_ci#define IST3038C_Y_MASK GENMASK(11, 0) 2862306a36Sopenharmony_ci#define IST3038C_AREA_MASK GENMASK(27, 24) 2962306a36Sopenharmony_ci#define IST3038C_AREA_SHIFT 24 3062306a36Sopenharmony_ci#define IST3038C_FINGER_COUNT_MASK GENMASK(15, 12) 3162306a36Sopenharmony_ci#define IST3038C_FINGER_COUNT_SHIFT 12 3262306a36Sopenharmony_ci#define IST3038C_FINGER_STATUS_MASK GENMASK(9, 0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct imagis_ts { 3562306a36Sopenharmony_ci struct i2c_client *client; 3662306a36Sopenharmony_ci struct input_dev *input_dev; 3762306a36Sopenharmony_ci struct touchscreen_properties prop; 3862306a36Sopenharmony_ci struct regulator_bulk_data supplies[2]; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int imagis_i2c_read_reg(struct imagis_ts *ts, 4262306a36Sopenharmony_ci unsigned int reg, u32 *data) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci __be32 ret_be; 4562306a36Sopenharmony_ci __be32 reg_be = cpu_to_be32(reg); 4662306a36Sopenharmony_ci struct i2c_msg msg[] = { 4762306a36Sopenharmony_ci { 4862306a36Sopenharmony_ci .addr = ts->client->addr, 4962306a36Sopenharmony_ci .flags = 0, 5062306a36Sopenharmony_ci .buf = (unsigned char *)®_be, 5162306a36Sopenharmony_ci .len = sizeof(reg_be), 5262306a36Sopenharmony_ci }, { 5362306a36Sopenharmony_ci .addr = ts->client->addr, 5462306a36Sopenharmony_ci .flags = I2C_M_RD, 5562306a36Sopenharmony_ci .buf = (unsigned char *)&ret_be, 5662306a36Sopenharmony_ci .len = sizeof(ret_be), 5762306a36Sopenharmony_ci }, 5862306a36Sopenharmony_ci }; 5962306a36Sopenharmony_ci int ret, error; 6062306a36Sopenharmony_ci int retry = IST3038C_I2C_RETRY_COUNT; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* Retry in case the controller fails to respond */ 6362306a36Sopenharmony_ci do { 6462306a36Sopenharmony_ci ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); 6562306a36Sopenharmony_ci if (ret == ARRAY_SIZE(msg)) { 6662306a36Sopenharmony_ci *data = be32_to_cpu(ret_be); 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci error = ret < 0 ? ret : -EIO; 7162306a36Sopenharmony_ci dev_err(&ts->client->dev, 7262306a36Sopenharmony_ci "%s - i2c_transfer failed: %d (%d)\n", 7362306a36Sopenharmony_ci __func__, error, ret); 7462306a36Sopenharmony_ci } while (--retry); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return error; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic irqreturn_t imagis_interrupt(int irq, void *dev_id) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct imagis_ts *ts = dev_id; 8262306a36Sopenharmony_ci u32 intr_message, finger_status; 8362306a36Sopenharmony_ci unsigned int finger_count, finger_pressed; 8462306a36Sopenharmony_ci int i; 8562306a36Sopenharmony_ci int error; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci error = imagis_i2c_read_reg(ts, IST3038C_REG_INTR_MESSAGE, 8862306a36Sopenharmony_ci &intr_message); 8962306a36Sopenharmony_ci if (error) { 9062306a36Sopenharmony_ci dev_err(&ts->client->dev, 9162306a36Sopenharmony_ci "failed to read the interrupt message: %d\n", error); 9262306a36Sopenharmony_ci goto out; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci finger_count = (intr_message & IST3038C_FINGER_COUNT_MASK) >> 9662306a36Sopenharmony_ci IST3038C_FINGER_COUNT_SHIFT; 9762306a36Sopenharmony_ci if (finger_count > IST3038C_MAX_FINGER_NUM) { 9862306a36Sopenharmony_ci dev_err(&ts->client->dev, 9962306a36Sopenharmony_ci "finger count %d is more than maximum supported\n", 10062306a36Sopenharmony_ci finger_count); 10162306a36Sopenharmony_ci goto out; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci finger_pressed = intr_message & IST3038C_FINGER_STATUS_MASK; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (i = 0; i < finger_count; i++) { 10762306a36Sopenharmony_ci error = imagis_i2c_read_reg(ts, 10862306a36Sopenharmony_ci IST3038C_REG_TOUCH_COORD + (i * 4), 10962306a36Sopenharmony_ci &finger_status); 11062306a36Sopenharmony_ci if (error) { 11162306a36Sopenharmony_ci dev_err(&ts->client->dev, 11262306a36Sopenharmony_ci "failed to read coordinates for finger %d: %d\n", 11362306a36Sopenharmony_ci i, error); 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci input_mt_slot(ts->input_dev, i); 11862306a36Sopenharmony_ci input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 11962306a36Sopenharmony_ci finger_pressed & BIT(i)); 12062306a36Sopenharmony_ci touchscreen_report_pos(ts->input_dev, &ts->prop, 12162306a36Sopenharmony_ci (finger_status & IST3038C_X_MASK) >> 12262306a36Sopenharmony_ci IST3038C_X_SHIFT, 12362306a36Sopenharmony_ci finger_status & IST3038C_Y_MASK, 1); 12462306a36Sopenharmony_ci input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 12562306a36Sopenharmony_ci (finger_status & IST3038C_AREA_MASK) >> 12662306a36Sopenharmony_ci IST3038C_AREA_SHIFT); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci input_mt_sync_frame(ts->input_dev); 13062306a36Sopenharmony_ci input_sync(ts->input_dev); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciout: 13362306a36Sopenharmony_ci return IRQ_HANDLED; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic void imagis_power_off(void *_ts) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct imagis_ts *ts = _ts; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(ts->supplies), ts->supplies); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic int imagis_power_on(struct imagis_ts *ts) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int error; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci error = regulator_bulk_enable(ARRAY_SIZE(ts->supplies), ts->supplies); 14862306a36Sopenharmony_ci if (error) 14962306a36Sopenharmony_ci return error; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci msleep(IST3038C_CHIP_ON_DELAY_MS); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic int imagis_start(struct imagis_ts *ts) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int error; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci error = imagis_power_on(ts); 16162306a36Sopenharmony_ci if (error) 16262306a36Sopenharmony_ci return error; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci enable_irq(ts->client->irq); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return 0; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int imagis_stop(struct imagis_ts *ts) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci disable_irq(ts->client->irq); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci imagis_power_off(ts); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int imagis_input_open(struct input_dev *dev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct imagis_ts *ts = input_get_drvdata(dev); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return imagis_start(ts); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void imagis_input_close(struct input_dev *dev) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct imagis_ts *ts = input_get_drvdata(dev); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci imagis_stop(ts); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int imagis_init_input_dev(struct imagis_ts *ts) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct input_dev *input_dev; 19562306a36Sopenharmony_ci int error; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci input_dev = devm_input_allocate_device(&ts->client->dev); 19862306a36Sopenharmony_ci if (!input_dev) 19962306a36Sopenharmony_ci return -ENOMEM; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ts->input_dev = input_dev; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci input_dev->name = "Imagis capacitive touchscreen"; 20462306a36Sopenharmony_ci input_dev->phys = "input/ts"; 20562306a36Sopenharmony_ci input_dev->id.bustype = BUS_I2C; 20662306a36Sopenharmony_ci input_dev->open = imagis_input_open; 20762306a36Sopenharmony_ci input_dev->close = imagis_input_close; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci input_set_drvdata(input_dev, ts); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); 21262306a36Sopenharmony_ci input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); 21362306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci touchscreen_parse_properties(input_dev, true, &ts->prop); 21662306a36Sopenharmony_ci if (!ts->prop.max_x || !ts->prop.max_y) { 21762306a36Sopenharmony_ci dev_err(&ts->client->dev, 21862306a36Sopenharmony_ci "Touchscreen-size-x and/or touchscreen-size-y not set in dts\n"); 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci error = input_mt_init_slots(input_dev, 22362306a36Sopenharmony_ci IST3038C_MAX_FINGER_NUM, 22462306a36Sopenharmony_ci INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 22562306a36Sopenharmony_ci if (error) { 22662306a36Sopenharmony_ci dev_err(&ts->client->dev, 22762306a36Sopenharmony_ci "Failed to initialize MT slots: %d", error); 22862306a36Sopenharmony_ci return error; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci error = input_register_device(input_dev); 23262306a36Sopenharmony_ci if (error) { 23362306a36Sopenharmony_ci dev_err(&ts->client->dev, 23462306a36Sopenharmony_ci "Failed to register input device: %d", error); 23562306a36Sopenharmony_ci return error; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int imagis_init_regulators(struct imagis_ts *ts) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct i2c_client *client = ts->client; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ts->supplies[0].supply = "vdd"; 24662306a36Sopenharmony_ci ts->supplies[1].supply = "vddio"; 24762306a36Sopenharmony_ci return devm_regulator_bulk_get(&client->dev, 24862306a36Sopenharmony_ci ARRAY_SIZE(ts->supplies), 24962306a36Sopenharmony_ci ts->supplies); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int imagis_probe(struct i2c_client *i2c) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct device *dev = &i2c->dev; 25562306a36Sopenharmony_ci struct imagis_ts *ts; 25662306a36Sopenharmony_ci int chip_id, error; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); 25962306a36Sopenharmony_ci if (!ts) 26062306a36Sopenharmony_ci return -ENOMEM; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ts->client = i2c; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci error = imagis_init_regulators(ts); 26562306a36Sopenharmony_ci if (error) { 26662306a36Sopenharmony_ci dev_err(dev, "regulator init error: %d\n", error); 26762306a36Sopenharmony_ci return error; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci error = imagis_power_on(ts); 27162306a36Sopenharmony_ci if (error) { 27262306a36Sopenharmony_ci dev_err(dev, "failed to enable regulators: %d\n", error); 27362306a36Sopenharmony_ci return error; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci error = devm_add_action_or_reset(dev, imagis_power_off, ts); 27762306a36Sopenharmony_ci if (error) { 27862306a36Sopenharmony_ci dev_err(dev, "failed to install poweroff action: %d\n", error); 27962306a36Sopenharmony_ci return error; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci error = imagis_i2c_read_reg(ts, 28362306a36Sopenharmony_ci IST3038C_REG_CHIPID | IST3038C_DIRECT_ACCESS, 28462306a36Sopenharmony_ci &chip_id); 28562306a36Sopenharmony_ci if (error) { 28662306a36Sopenharmony_ci dev_err(dev, "chip ID read failure: %d\n", error); 28762306a36Sopenharmony_ci return error; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (chip_id != IST3038C_WHOAMI) { 29162306a36Sopenharmony_ci dev_err(dev, "unknown chip ID: 0x%x\n", chip_id); 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci error = devm_request_threaded_irq(dev, i2c->irq, 29662306a36Sopenharmony_ci NULL, imagis_interrupt, 29762306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_NO_AUTOEN, 29862306a36Sopenharmony_ci "imagis-touchscreen", ts); 29962306a36Sopenharmony_ci if (error) { 30062306a36Sopenharmony_ci dev_err(dev, "IRQ %d allocation failure: %d\n", 30162306a36Sopenharmony_ci i2c->irq, error); 30262306a36Sopenharmony_ci return error; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci error = imagis_init_input_dev(ts); 30662306a36Sopenharmony_ci if (error) 30762306a36Sopenharmony_ci return error; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int imagis_suspend(struct device *dev) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 31562306a36Sopenharmony_ci struct imagis_ts *ts = i2c_get_clientdata(client); 31662306a36Sopenharmony_ci int retval = 0; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci mutex_lock(&ts->input_dev->mutex); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (input_device_enabled(ts->input_dev)) 32162306a36Sopenharmony_ci retval = imagis_stop(ts); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci mutex_unlock(&ts->input_dev->mutex); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return retval; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int imagis_resume(struct device *dev) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 33162306a36Sopenharmony_ci struct imagis_ts *ts = i2c_get_clientdata(client); 33262306a36Sopenharmony_ci int retval = 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci mutex_lock(&ts->input_dev->mutex); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (input_device_enabled(ts->input_dev)) 33762306a36Sopenharmony_ci retval = imagis_start(ts); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci mutex_unlock(&ts->input_dev->mutex); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return retval; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(imagis_pm_ops, imagis_suspend, imagis_resume); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci#ifdef CONFIG_OF 34762306a36Sopenharmony_cistatic const struct of_device_id imagis_of_match[] = { 34862306a36Sopenharmony_ci { .compatible = "imagis,ist3038c", }, 34962306a36Sopenharmony_ci { }, 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imagis_of_match); 35262306a36Sopenharmony_ci#endif 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic struct i2c_driver imagis_ts_driver = { 35562306a36Sopenharmony_ci .driver = { 35662306a36Sopenharmony_ci .name = "imagis-touchscreen", 35762306a36Sopenharmony_ci .pm = pm_sleep_ptr(&imagis_pm_ops), 35862306a36Sopenharmony_ci .of_match_table = of_match_ptr(imagis_of_match), 35962306a36Sopenharmony_ci }, 36062306a36Sopenharmony_ci .probe = imagis_probe, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cimodule_i2c_driver(imagis_ts_driver); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ciMODULE_DESCRIPTION("Imagis IST3038C Touchscreen Driver"); 36662306a36Sopenharmony_ciMODULE_AUTHOR("Markuss Broks <markuss.broks@gmail.com>"); 36762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 368