Files
linux-stable-mirror/drivers/input/touchscreen/cy8ctmg110_ts.c
T
Uwe Kleine-König (The Capable Hub)andDmitry Torokhov 47ceab218c Input: Use named initializers for arrays of i2c_device_data
While being less compact, using named initializers allows to more easily
see which members of the structs are assigned which value without having
to lookup the declaration of the struct. And it's also more robust
against changes to the struct definition.

The mentioned robustness is relevant for a planned change to struct
i2c_device_id that replaces .driver_data by an anonymous union.

This patch doesn't modify the compiled arrays, only their representation
in source form benefits. The former was confirmed with x86 and arm64
builds.

Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
Link: https://patch.msgid.link/20260515164848.497608-2-u.kleine-koenig@baylibre.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
2026-05-30 22:10:47 -07:00

290 lines
6.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for cypress touch screen controller
*
* Copyright (c) 2009 Aava Mobile
*
* Some cleanups by Alan Cox <alan@linux.intel.com>
*/
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/byteorder.h>
#define CY8CTMG110_DRIVER_NAME "cy8ctmg110"
/* Touch coordinates */
#define CY8CTMG110_X_MIN 0
#define CY8CTMG110_Y_MIN 0
#define CY8CTMG110_X_MAX 759
#define CY8CTMG110_Y_MAX 465
/* cy8ctmg110 register definitions */
#define CY8CTMG110_TOUCH_WAKEUP_TIME 0
#define CY8CTMG110_TOUCH_SLEEP_TIME 2
#define CY8CTMG110_TOUCH_X1 3
#define CY8CTMG110_TOUCH_Y1 5
#define CY8CTMG110_TOUCH_X2 7
#define CY8CTMG110_TOUCH_Y2 9
#define CY8CTMG110_FINGERS 11
#define CY8CTMG110_GESTURE 12
#define CY8CTMG110_REG_MAX 13
/*
* The touch driver structure.
*/
struct cy8ctmg110 {
struct input_dev *input;
char phys[32];
struct i2c_client *client;
struct gpio_desc *reset_gpio;
};
/*
* cy8ctmg110_power is the routine that is called when touch hardware
* is being powered off or on. When powering on this routine de-asserts
* the RESET line, when powering off reset line is asserted.
*/
static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron)
{
if (ts->reset_gpio)
gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
}
static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg,
unsigned char len, unsigned char *value)
{
struct i2c_client *client = tsc->client;
int ret;
unsigned char i2c_data[6];
BUG_ON(len > 5);
i2c_data[0] = reg;
memcpy(i2c_data + 1, value, len);
ret = i2c_master_send(client, i2c_data, len + 1);
if (ret != len + 1) {
dev_err(&client->dev, "i2c write data cmd failed\n");
return ret < 0 ? ret : -EIO;
}
return 0;
}
static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc,
unsigned char *data, unsigned char len, unsigned char cmd)
{
struct i2c_client *client = tsc->client;
int ret;
struct i2c_msg msg[2] = {
/* first write slave position to i2c devices */
{
.addr = client->addr,
.len = 1,
.buf = &cmd
},
/* Second read data from position */
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = len,
.buf = data
}
};
ret = i2c_transfer(client->adapter, msg, 2);
if (ret < 0)
return ret;
return 0;
}
static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc)
{
struct input_dev *input = tsc->input;
unsigned char reg_p[CY8CTMG110_REG_MAX];
memset(reg_p, 0, CY8CTMG110_REG_MAX);
/* Reading coordinates */
if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0)
return -EIO;
/* Number of touch */
if (reg_p[8] == 0) {
input_report_key(input, BTN_TOUCH, 0);
} else {
input_report_key(input, BTN_TOUCH, 1);
input_report_abs(input, ABS_X,
be16_to_cpup((__be16 *)(reg_p + 0)));
input_report_abs(input, ABS_Y,
be16_to_cpup((__be16 *)(reg_p + 2)));
}
input_sync(input);
return 0;
}
static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep)
{
unsigned char reg_p[3];
if (sleep) {
reg_p[0] = 0x00;
reg_p[1] = 0xff;
reg_p[2] = 5;
} else {
reg_p[0] = 0x10;
reg_p[1] = 0xff;
reg_p[2] = 0;
}
return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p);
}
static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id)
{
struct cy8ctmg110 *tsc = dev_id;
cy8ctmg110_touch_pos(tsc);
return IRQ_HANDLED;
}
static void cy8ctmg110_shut_off(void *_ts)
{
struct cy8ctmg110 *ts = _ts;
cy8ctmg110_set_sleepmode(ts, true);
cy8ctmg110_power(ts, false);
}
static int cy8ctmg110_probe(struct i2c_client *client)
{
struct cy8ctmg110 *ts;
struct input_dev *input_dev;
int err;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA))
return -EIO;
ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
input_dev = devm_input_allocate_device(&client->dev);
if (!input_dev)
return -ENOMEM;
ts->client = client;
ts->input = input_dev;
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = BUS_I2C;
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X,
CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
input_set_abs_params(input_dev, ABS_Y,
CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
/* Request and assert reset line */
ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL,
GPIOD_OUT_HIGH);
if (IS_ERR(ts->reset_gpio)) {
err = PTR_ERR(ts->reset_gpio);
dev_err(&client->dev,
"Unable to request reset GPIO: %d\n", err);
return err;
}
cy8ctmg110_power(ts, true);
cy8ctmg110_set_sleepmode(ts, false);
err = devm_add_action_or_reset(&client->dev, cy8ctmg110_shut_off, ts);
if (err)
return err;
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, cy8ctmg110_irq_thread,
IRQF_ONESHOT, "touch_reset_key", ts);
if (err) {
dev_err(&client->dev,
"irq %d busy? error %d\n", client->irq, err);
return err;
}
err = input_register_device(input_dev);
if (err)
return err;
i2c_set_clientdata(client, ts);
return 0;
}
static int cy8ctmg110_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
if (!device_may_wakeup(&client->dev)) {
cy8ctmg110_set_sleepmode(ts, true);
cy8ctmg110_power(ts, false);
}
return 0;
}
static int cy8ctmg110_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
if (!device_may_wakeup(&client->dev)) {
cy8ctmg110_power(ts, true);
cy8ctmg110_set_sleepmode(ts, false);
}
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(cy8ctmg110_pm,
cy8ctmg110_suspend, cy8ctmg110_resume);
static const struct i2c_device_id cy8ctmg110_idtable[] = {
{ .name = CY8CTMG110_DRIVER_NAME, .driver_data = 1 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable);
static struct i2c_driver cy8ctmg110_driver = {
.driver = {
.name = CY8CTMG110_DRIVER_NAME,
.pm = pm_sleep_ptr(&cy8ctmg110_pm),
},
.id_table = cy8ctmg110_idtable,
.probe = cy8ctmg110_probe,
};
module_i2c_driver(cy8ctmg110_driver);
MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");
MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");
MODULE_LICENSE("GPL v2");