mirror of
https://github.com/torvalds/linux.git
synced 2026-04-22 00:33:58 -04:00
Export a sysfs interface that would allow reading and writing touchscreen IC registers. With this interface many things can be done in usersapce such as firmware updates. An example tool that utilizes this interface for performing firmware updates can be found at [1]. [1] https://github.com/goodix/fwupdate_for_berlin_linux Signed-off-by: Charles Wang <charles.goodix@gmail.com> Link: https://lore.kernel.org/r/20240514115135.21410-1-charles.goodix@gmail.com Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
182 lines
4.9 KiB
C
182 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Goodix Berlin Touchscreen Driver
|
|
*
|
|
* Copyright (C) 2020 - 2021 Goodix, Inc.
|
|
* Copyright (C) 2023 Linaro Ltd.
|
|
*
|
|
* Based on goodix_ts_berlin driver.
|
|
*/
|
|
#include <asm/unaligned.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/input.h>
|
|
|
|
#include "goodix_berlin.h"
|
|
|
|
#define GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN 1
|
|
#define GOODIX_BERLIN_REGISTER_WIDTH 4
|
|
#define GOODIX_BERLIN_SPI_READ_DUMMY_LEN 3
|
|
#define GOODIX_BERLIN_SPI_READ_PREFIX_LEN (GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + \
|
|
GOODIX_BERLIN_REGISTER_WIDTH + \
|
|
GOODIX_BERLIN_SPI_READ_DUMMY_LEN)
|
|
#define GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN (GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + \
|
|
GOODIX_BERLIN_REGISTER_WIDTH)
|
|
|
|
#define GOODIX_BERLIN_SPI_WRITE_FLAG 0xF0
|
|
#define GOODIX_BERLIN_SPI_READ_FLAG 0xF1
|
|
|
|
static int goodix_berlin_spi_read(void *context, const void *reg_buf,
|
|
size_t reg_size, void *val_buf,
|
|
size_t val_size)
|
|
{
|
|
struct spi_device *spi = context;
|
|
struct spi_transfer xfers;
|
|
struct spi_message spi_msg;
|
|
const u32 *reg = reg_buf; /* reg is stored as native u32 at start of buffer */
|
|
int error;
|
|
|
|
if (reg_size != GOODIX_BERLIN_REGISTER_WIDTH)
|
|
return -EINVAL;
|
|
|
|
u8 *buf __free(kfree) =
|
|
kzalloc(GOODIX_BERLIN_SPI_READ_PREFIX_LEN + val_size,
|
|
GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
spi_message_init(&spi_msg);
|
|
memset(&xfers, 0, sizeof(xfers));
|
|
|
|
/* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */
|
|
buf[0] = GOODIX_BERLIN_SPI_READ_FLAG;
|
|
put_unaligned_be32(*reg, buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN);
|
|
memset(buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN + GOODIX_BERLIN_REGISTER_WIDTH,
|
|
0xff, GOODIX_BERLIN_SPI_READ_DUMMY_LEN);
|
|
|
|
xfers.tx_buf = buf;
|
|
xfers.rx_buf = buf;
|
|
xfers.len = GOODIX_BERLIN_SPI_READ_PREFIX_LEN + val_size;
|
|
xfers.cs_change = 0;
|
|
spi_message_add_tail(&xfers, &spi_msg);
|
|
|
|
error = spi_sync(spi, &spi_msg);
|
|
if (error < 0) {
|
|
dev_err(&spi->dev, "spi transfer error, %d", error);
|
|
return error;
|
|
}
|
|
|
|
memcpy(val_buf, buf + GOODIX_BERLIN_SPI_READ_PREFIX_LEN, val_size);
|
|
return error;
|
|
}
|
|
|
|
static int goodix_berlin_spi_write(void *context, const void *data,
|
|
size_t count)
|
|
{
|
|
unsigned int len = count - GOODIX_BERLIN_REGISTER_WIDTH;
|
|
struct spi_device *spi = context;
|
|
struct spi_transfer xfers;
|
|
struct spi_message spi_msg;
|
|
const u32 *reg = data; /* reg is stored as native u32 at start of buffer */
|
|
int error;
|
|
|
|
u8 *buf __free(kfree) =
|
|
kzalloc(GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
spi_message_init(&spi_msg);
|
|
memset(&xfers, 0, sizeof(xfers));
|
|
|
|
buf[0] = GOODIX_BERLIN_SPI_WRITE_FLAG;
|
|
put_unaligned_be32(*reg, buf + GOODIX_BERLIN_SPI_TRANS_PREFIX_LEN);
|
|
memcpy(buf + GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN,
|
|
data + GOODIX_BERLIN_REGISTER_WIDTH, len);
|
|
|
|
xfers.tx_buf = buf;
|
|
xfers.len = GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN + len;
|
|
xfers.cs_change = 0;
|
|
spi_message_add_tail(&xfers, &spi_msg);
|
|
|
|
error = spi_sync(spi, &spi_msg);
|
|
if (error < 0) {
|
|
dev_err(&spi->dev, "spi transfer error, %d", error);
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct regmap_config goodix_berlin_spi_regmap_conf = {
|
|
.reg_bits = 32,
|
|
.val_bits = 8,
|
|
.read = goodix_berlin_spi_read,
|
|
.write = goodix_berlin_spi_write,
|
|
};
|
|
|
|
/* vendor & product left unassigned here, should probably be updated from fw info */
|
|
static const struct input_id goodix_berlin_spi_input_id = {
|
|
.bustype = BUS_SPI,
|
|
};
|
|
|
|
static int goodix_berlin_spi_probe(struct spi_device *spi)
|
|
{
|
|
struct regmap_config regmap_config;
|
|
struct regmap *regmap;
|
|
size_t max_size;
|
|
int error = 0;
|
|
|
|
spi->mode = SPI_MODE_0;
|
|
spi->bits_per_word = 8;
|
|
error = spi_setup(spi);
|
|
if (error)
|
|
return error;
|
|
|
|
max_size = spi_max_transfer_size(spi);
|
|
|
|
regmap_config = goodix_berlin_spi_regmap_conf;
|
|
regmap_config.max_raw_read = max_size - GOODIX_BERLIN_SPI_READ_PREFIX_LEN;
|
|
regmap_config.max_raw_write = max_size - GOODIX_BERLIN_SPI_WRITE_PREFIX_LEN;
|
|
|
|
regmap = devm_regmap_init(&spi->dev, NULL, spi, ®map_config);
|
|
if (IS_ERR(regmap))
|
|
return PTR_ERR(regmap);
|
|
|
|
error = goodix_berlin_probe(&spi->dev, spi->irq,
|
|
&goodix_berlin_spi_input_id, regmap);
|
|
if (error)
|
|
return error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct spi_device_id goodix_berlin_spi_ids[] = {
|
|
{ "gt9916" },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, goodix_berlin_spi_ids);
|
|
|
|
static const struct of_device_id goodix_berlin_spi_of_match[] = {
|
|
{ .compatible = "goodix,gt9916", },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, goodix_berlin_spi_of_match);
|
|
|
|
static struct spi_driver goodix_berlin_spi_driver = {
|
|
.driver = {
|
|
.name = "goodix-berlin-spi",
|
|
.of_match_table = goodix_berlin_spi_of_match,
|
|
.pm = pm_sleep_ptr(&goodix_berlin_pm_ops),
|
|
.dev_groups = goodix_berlin_groups,
|
|
},
|
|
.probe = goodix_berlin_spi_probe,
|
|
.id_table = goodix_berlin_spi_ids,
|
|
};
|
|
module_spi_driver(goodix_berlin_spi_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Goodix Berlin SPI Touchscreen driver");
|
|
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
|