162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Touchscreen driver for the tps6507x chip. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2009 RidgeRun (todd.fischer@ridgerun.com) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Credits: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Using code from tsc2007, MtekVision Co., Ltd. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * For licencing details see kernel-base/COPYING 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * TPS65070, TPS65073, TPS650731, and TPS650732 support 1362306a36Sopenharmony_ci * 10 bit touch screen interface. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/workqueue.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/input.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/mfd/tps6507x.h> 2262306a36Sopenharmony_ci#include <linux/input/tps6507x-ts.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define TSC_DEFAULT_POLL_PERIOD 30 /* ms */ 2662306a36Sopenharmony_ci#define TPS_DEFAULT_MIN_PRESSURE 0x30 2762306a36Sopenharmony_ci#define MAX_10BIT ((1 << 10) - 1) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define TPS6507X_ADCONFIG_CONVERT_TS (TPS6507X_ADCONFIG_AD_ENABLE | \ 3062306a36Sopenharmony_ci TPS6507X_ADCONFIG_START_CONVERSION | \ 3162306a36Sopenharmony_ci TPS6507X_ADCONFIG_INPUT_REAL_TSC) 3262306a36Sopenharmony_ci#define TPS6507X_ADCONFIG_POWER_DOWN_TS (TPS6507X_ADCONFIG_INPUT_REAL_TSC) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct ts_event { 3562306a36Sopenharmony_ci u16 x; 3662306a36Sopenharmony_ci u16 y; 3762306a36Sopenharmony_ci u16 pressure; 3862306a36Sopenharmony_ci}; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct tps6507x_ts { 4162306a36Sopenharmony_ci struct device *dev; 4262306a36Sopenharmony_ci struct input_dev *input; 4362306a36Sopenharmony_ci struct tps6507x_dev *mfd; 4462306a36Sopenharmony_ci char phys[32]; 4562306a36Sopenharmony_ci struct ts_event tc; 4662306a36Sopenharmony_ci u16 min_pressure; 4762306a36Sopenharmony_ci bool pendown; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int tps6507x_read_u8(struct tps6507x_ts *tsc, u8 reg, u8 *data) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci return tsc->mfd->read_dev(tsc->mfd, reg, 1, data); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int tps6507x_write_u8(struct tps6507x_ts *tsc, u8 reg, u8 data) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return tsc->mfd->write_dev(tsc->mfd, reg, 1, &data); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic s32 tps6507x_adc_conversion(struct tps6507x_ts *tsc, 6162306a36Sopenharmony_ci u8 tsc_mode, u16 *value) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci s32 ret; 6462306a36Sopenharmony_ci u8 adc_status; 6562306a36Sopenharmony_ci u8 result; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Route input signal to A/D converter */ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, tsc_mode); 7062306a36Sopenharmony_ci if (ret) { 7162306a36Sopenharmony_ci dev_err(tsc->dev, "TSC mode read failed\n"); 7262306a36Sopenharmony_ci goto err; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Start A/D conversion */ 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, 7862306a36Sopenharmony_ci TPS6507X_ADCONFIG_CONVERT_TS); 7962306a36Sopenharmony_ci if (ret) { 8062306a36Sopenharmony_ci dev_err(tsc->dev, "ADC config write failed\n"); 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci do { 8562306a36Sopenharmony_ci ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADCONFIG, 8662306a36Sopenharmony_ci &adc_status); 8762306a36Sopenharmony_ci if (ret) { 8862306a36Sopenharmony_ci dev_err(tsc->dev, "ADC config read failed\n"); 8962306a36Sopenharmony_ci goto err; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci } while (adc_status & TPS6507X_ADCONFIG_START_CONVERSION); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_2, &result); 9462306a36Sopenharmony_ci if (ret) { 9562306a36Sopenharmony_ci dev_err(tsc->dev, "ADC result 2 read failed\n"); 9662306a36Sopenharmony_ci goto err; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci *value = (result & TPS6507X_REG_ADRESULT_2_MASK) << 8; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ret = tps6507x_read_u8(tsc, TPS6507X_REG_ADRESULT_1, &result); 10262306a36Sopenharmony_ci if (ret) { 10362306a36Sopenharmony_ci dev_err(tsc->dev, "ADC result 1 read failed\n"); 10462306a36Sopenharmony_ci goto err; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci *value |= result; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci dev_dbg(tsc->dev, "TSC channel %d = 0x%X\n", tsc_mode, *value); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cierr: 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Need to call tps6507x_adc_standby() after using A/D converter for the 11662306a36Sopenharmony_ci * touch screen interrupt to work properly. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci s32 ret; 12262306a36Sopenharmony_ci u8 val; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci ret = tps6507x_write_u8(tsc, TPS6507X_REG_ADCONFIG, 12562306a36Sopenharmony_ci TPS6507X_ADCONFIG_INPUT_TSC); 12662306a36Sopenharmony_ci if (ret) 12762306a36Sopenharmony_ci return ret; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = tps6507x_write_u8(tsc, TPS6507X_REG_TSCMODE, 13062306a36Sopenharmony_ci TPS6507X_TSCMODE_STANDBY); 13162306a36Sopenharmony_ci if (ret) 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); 13562306a36Sopenharmony_ci if (ret) 13662306a36Sopenharmony_ci return ret; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci while (val & TPS6507X_REG_TSC_INT) { 13962306a36Sopenharmony_ci mdelay(10); 14062306a36Sopenharmony_ci ret = tps6507x_read_u8(tsc, TPS6507X_REG_INT, &val); 14162306a36Sopenharmony_ci if (ret) 14262306a36Sopenharmony_ci return ret; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return ret; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void tps6507x_ts_poll(struct input_dev *input_dev) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct tps6507x_ts *tsc = input_get_drvdata(input_dev); 15162306a36Sopenharmony_ci bool pendown; 15262306a36Sopenharmony_ci s32 ret; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_PRESSURE, 15562306a36Sopenharmony_ci &tsc->tc.pressure); 15662306a36Sopenharmony_ci if (ret) 15762306a36Sopenharmony_ci goto done; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci pendown = tsc->tc.pressure > tsc->min_pressure; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (unlikely(!pendown && tsc->pendown)) { 16262306a36Sopenharmony_ci dev_dbg(tsc->dev, "UP\n"); 16362306a36Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 0); 16462306a36Sopenharmony_ci input_report_abs(input_dev, ABS_PRESSURE, 0); 16562306a36Sopenharmony_ci input_sync(input_dev); 16662306a36Sopenharmony_ci tsc->pendown = false; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (pendown) { 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!tsc->pendown) { 17262306a36Sopenharmony_ci dev_dbg(tsc->dev, "DOWN\n"); 17362306a36Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 1); 17462306a36Sopenharmony_ci } else 17562306a36Sopenharmony_ci dev_dbg(tsc->dev, "still down\n"); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_X_POSITION, 17862306a36Sopenharmony_ci &tsc->tc.x); 17962306a36Sopenharmony_ci if (ret) 18062306a36Sopenharmony_ci goto done; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ret = tps6507x_adc_conversion(tsc, TPS6507X_TSCMODE_Y_POSITION, 18362306a36Sopenharmony_ci &tsc->tc.y); 18462306a36Sopenharmony_ci if (ret) 18562306a36Sopenharmony_ci goto done; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci input_report_abs(input_dev, ABS_X, tsc->tc.x); 18862306a36Sopenharmony_ci input_report_abs(input_dev, ABS_Y, tsc->tc.y); 18962306a36Sopenharmony_ci input_report_abs(input_dev, ABS_PRESSURE, tsc->tc.pressure); 19062306a36Sopenharmony_ci input_sync(input_dev); 19162306a36Sopenharmony_ci tsc->pendown = true; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cidone: 19562306a36Sopenharmony_ci tps6507x_adc_standby(tsc); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int tps6507x_ts_probe(struct platform_device *pdev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct tps6507x_dev *tps6507x_dev = dev_get_drvdata(pdev->dev.parent); 20162306a36Sopenharmony_ci const struct tps6507x_board *tps_board; 20262306a36Sopenharmony_ci const struct touchscreen_init_data *init_data; 20362306a36Sopenharmony_ci struct tps6507x_ts *tsc; 20462306a36Sopenharmony_ci struct input_dev *input_dev; 20562306a36Sopenharmony_ci int error; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* 20862306a36Sopenharmony_ci * tps_board points to pmic related constants 20962306a36Sopenharmony_ci * coming from the board-evm file. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci tps_board = dev_get_platdata(tps6507x_dev->dev); 21262306a36Sopenharmony_ci if (!tps_board) { 21362306a36Sopenharmony_ci dev_err(tps6507x_dev->dev, 21462306a36Sopenharmony_ci "Could not find tps6507x platform data\n"); 21562306a36Sopenharmony_ci return -ENODEV; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * init_data points to array of regulator_init structures 22062306a36Sopenharmony_ci * coming from the board-evm file. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci init_data = tps_board->tps6507x_ts_init_data; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL); 22562306a36Sopenharmony_ci if (!tsc) { 22662306a36Sopenharmony_ci dev_err(tps6507x_dev->dev, "failed to allocate driver data\n"); 22762306a36Sopenharmony_ci return -ENOMEM; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci tsc->mfd = tps6507x_dev; 23162306a36Sopenharmony_ci tsc->dev = tps6507x_dev->dev; 23262306a36Sopenharmony_ci tsc->min_pressure = init_data ? 23362306a36Sopenharmony_ci init_data->min_pressure : TPS_DEFAULT_MIN_PRESSURE; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci snprintf(tsc->phys, sizeof(tsc->phys), 23662306a36Sopenharmony_ci "%s/input0", dev_name(tsc->dev)); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci input_dev = devm_input_allocate_device(&pdev->dev); 23962306a36Sopenharmony_ci if (!input_dev) { 24062306a36Sopenharmony_ci dev_err(tsc->dev, "Failed to allocate polled input device.\n"); 24162306a36Sopenharmony_ci return -ENOMEM; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci tsc->input = input_dev; 24562306a36Sopenharmony_ci input_set_drvdata(input_dev, tsc); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 24862306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0); 24962306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0); 25062306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci input_dev->name = "TPS6507x Touchscreen"; 25362306a36Sopenharmony_ci input_dev->phys = tsc->phys; 25462306a36Sopenharmony_ci input_dev->dev.parent = tsc->dev; 25562306a36Sopenharmony_ci input_dev->id.bustype = BUS_I2C; 25662306a36Sopenharmony_ci if (init_data) { 25762306a36Sopenharmony_ci input_dev->id.vendor = init_data->vendor; 25862306a36Sopenharmony_ci input_dev->id.product = init_data->product; 25962306a36Sopenharmony_ci input_dev->id.version = init_data->version; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci error = tps6507x_adc_standby(tsc); 26362306a36Sopenharmony_ci if (error) 26462306a36Sopenharmony_ci return error; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci error = input_setup_polling(input_dev, tps6507x_ts_poll); 26762306a36Sopenharmony_ci if (error) 26862306a36Sopenharmony_ci return error; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci input_set_poll_interval(input_dev, 27162306a36Sopenharmony_ci init_data ? init_data->poll_period : 27262306a36Sopenharmony_ci TSC_DEFAULT_POLL_PERIOD); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci error = input_register_device(input_dev); 27562306a36Sopenharmony_ci if (error) 27662306a36Sopenharmony_ci return error; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic struct platform_driver tps6507x_ts_driver = { 28262306a36Sopenharmony_ci .driver = { 28362306a36Sopenharmony_ci .name = "tps6507x-ts", 28462306a36Sopenharmony_ci }, 28562306a36Sopenharmony_ci .probe = tps6507x_ts_probe, 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_cimodule_platform_driver(tps6507x_ts_driver); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciMODULE_AUTHOR("Todd Fischer <todd.fischer@ridgerun.com>"); 29062306a36Sopenharmony_ciMODULE_DESCRIPTION("TPS6507x - TouchScreen driver"); 29162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 29262306a36Sopenharmony_ciMODULE_ALIAS("platform:tps6507x-ts"); 293