Add I2C bus support to lcdreg.
Signed-off-by: Noralf Trønnes <[email protected]>
---
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
[email protected]
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel