Add I2C bus support to lcdreg.

Signed-off-by: Noralf Trønnes <nor...@tronnes.org>
---
 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
de...@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

Reply via email to