Add I2C bus support to lcdreg. Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> --- drivers/staging/fbtft/lcdreg/Kconfig | 6 ++ drivers/staging/fbtft/lcdreg/Makefile | 2 + drivers/staging/fbtft/lcdreg/lcdreg-i2c.c | 129 ++++++++++++++++++++++++++++++ drivers/staging/fbtft/lcdreg/lcdreg.h | 3 + 4 files changed, 140 insertions(+) create mode 100644 drivers/staging/fbtft/lcdreg/lcdreg-i2c.c diff --git a/drivers/staging/fbtft/lcdreg/Kconfig b/drivers/staging/fbtft/lcdreg/Kconfig index ec0c097..8f000b5 100644 --- a/drivers/staging/fbtft/lcdreg/Kconfig +++ b/drivers/staging/fbtft/lcdreg/Kconfig @@ -2,3 +2,9 @@ config LCDREG tristate depends on GPIOLIB default n + +config LCDREG_I2C + tristate + depends on I2C + select LCDREG + default n diff --git a/drivers/staging/fbtft/lcdreg/Makefile b/drivers/staging/fbtft/lcdreg/Makefile index c9ea774..1ec65af 100644 --- a/drivers/staging/fbtft/lcdreg/Makefile +++ b/drivers/staging/fbtft/lcdreg/Makefile @@ -1,3 +1,5 @@ obj-$(CONFIG_LCDREG) += lcdreg.o lcdreg-y += lcdreg-core.o lcdreg-$(CONFIG_DEBUG_FS) += lcdreg-debugfs.o + +obj-$(CONFIG_LCDREG_I2C) += lcdreg-i2c.o diff --git a/drivers/staging/fbtft/lcdreg/lcdreg-i2c.c b/drivers/staging/fbtft/lcdreg/lcdreg-i2c.c new file mode 100644 index 0000000..297926f --- /dev/null +++ b/drivers/staging/fbtft/lcdreg/lcdreg-i2c.c @@ -0,0 +1,129 @@ +/* + * lcdreg I2C support + * + * Copyright 2015 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include "lcdreg.h" + +struct lcdreg_i2c { + struct lcdreg reg; + struct i2c_client *client; + struct gpio_desc *reset; +}; + +static inline struct lcdreg_i2c *to_lcdreg_i2c(struct lcdreg *reg) +{ + return reg ? container_of(reg, struct lcdreg_i2c, reg) : NULL; +} + +static int lcdreg_i2c_send(struct i2c_client *client, unsigned index, + void *buf, size_t len) +{ + u8 *txbuf; + int ret; + + txbuf = kmalloc(1 + len, GFP_KERNEL); + if (!txbuf) + return -ENOMEM; + + txbuf[0] = index ? 0x40 : 0x80; + memcpy(&txbuf[1], buf, len); + + ret = i2c_master_send(client, txbuf, 1 + len); + kfree(txbuf); + + return ret < 0 ? ret : 0; +} + +static int lcdreg_i2c_write(struct lcdreg *reg, unsigned regnr, + struct lcdreg_transfer *transfer) +{ + struct lcdreg_i2c *i2c = to_lcdreg_i2c(reg); + u8 regnr_buf[1] = { regnr }; + int ret; + + ret = lcdreg_i2c_send(i2c->client, 0, regnr_buf, 1); + if (ret) + return ret; + + if (!transfer || !transfer->count) + return 0; + + if (WARN_ON(transfer->width != 8)) + return -EINVAL; + + if (!transfer->count) + return 0; + + return lcdreg_i2c_send(i2c->client, transfer->index, transfer->buf, + transfer->count); +} + +static int lcdreg_i2c_read(struct lcdreg *reg, unsigned regnr, + struct lcdreg_transfer *transfer) +{ + struct lcdreg_i2c *i2c = to_lcdreg_i2c(reg); + int ret; + + if (WARN_ON(regnr != 0 || !transfer || transfer->width != 8)) + return -EINVAL; + + if (!reg->readable) + return -EACCES; + + ret = i2c_master_recv(i2c->client, transfer->buf, transfer->count); + + return ret < 0 ? ret : 0; +} + +static void lcdreg_i2c_reset(struct lcdreg *reg) +{ + struct lcdreg_i2c *i2c = to_lcdreg_i2c(reg); + + if (!i2c->reset) + return; + + gpiod_set_value_cansleep(i2c->reset, 0); + msleep(20); + gpiod_set_value_cansleep(i2c->reset, 1); + msleep(120); +} + +struct lcdreg *devm_lcdreg_i2c_init(struct i2c_client *client) +{ + struct lcdreg_i2c *i2c; + + i2c = devm_kzalloc(&client->dev, sizeof(*i2c), GFP_KERNEL); + if (i2c == NULL) + return ERR_PTR(-ENOMEM); + + i2c->reg.readable = true; + i2c->client = client; + i2c->reset = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(i2c->reset)) + return ERR_PTR(PTR_ERR(i2c->reset)); + + i2c->reg.write = lcdreg_i2c_write; + i2c->reg.read = lcdreg_i2c_read; + i2c->reg.reset = lcdreg_i2c_reset; + + return devm_lcdreg_init(&client->dev, &i2c->reg); +} +EXPORT_SYMBOL(devm_lcdreg_i2c_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/fbtft/lcdreg/lcdreg.h b/drivers/staging/fbtft/lcdreg/lcdreg.h index 0ddf40e..4fa5aea 100644 --- a/drivers/staging/fbtft/lcdreg/lcdreg.h +++ b/drivers/staging/fbtft/lcdreg/lcdreg.h @@ -11,6 +11,7 @@ #define __LINUX_LCDREG_H #include <linux/device.h> +#include <linux/i2c.h> #include <linux/mutex.h> /** @@ -121,6 +122,8 @@ static inline unsigned lcdreg_bytes_per_word(unsigned bits_per_word) return 4; } +struct lcdreg *devm_lcdreg_i2c_init(struct i2c_client *client); + #if defined(CONFIG_DYNAMIC_DEBUG) #define lcdreg_dbg_transfer_buf(transfer) \ -- 2.2.2 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel