Permalink
Browse files

first commit

  • Loading branch information...
1 parent 2171c42 commit 4a441b89572fea622575c30a28359381a4ecef03 @notro committed Jan 18, 2013
Showing with 1,933 additions and 17 deletions.
  1. +83 −13 .gitignore
  2. +20 −0 Kconfig
  3. +22 −0 Makefile
  4. +78 −0 README
  5. +0 −4 README.md
  6. +273 −0 adafruit22fb.c
  7. +756 −0 fbtft.c
  8. +116 −0 fbtft.h
  9. +426 −0 sainsmart18fb.c
  10. +159 −0 spidevices.c
View
96 .gitignore
@@ -1,17 +1,87 @@
-# Object files
+# This file is copied from the Linux kernel sources
+#
+# NOTE! Don't add files that are generated in specific
+# subdirectories here. Add them in the ".gitignore" file
+# in that subdirectory instead.
+#
+# NOTE! Please use 'git ls-files -i --exclude-standard'
+# command after changing this file, to see if there are
+# any tracked files which get ignored after the change.
+#
+# Normal rules
+#
+.*
*.o
-
-# Libraries
-*.lib
+*.o.*
*.a
-
-# Shared objects (inc. Windows DLLs)
-*.dll
+*.s
+*.ko
*.so
-*.so.*
-*.dylib
+*.so.dbg
+*.mod.c
+*.i
+*.lst
+*.symtypes
+*.order
+modules.builtin
+*.elf
+*.bin
+*.gz
+*.bz2
+*.lzma
+*.xz
+*.lzo
+*.patch
+*.gcno
+
+#
+# Top-level generic files
+#
+/tags
+/TAGS
+/linux
+/vmlinux
+/vmlinuz
+/System.map
+/Module.markers
+/Module.symvers
+
+#
+# Debian directory (make deb-pkg)
+#
+/debian/
+
+#
+# git files that we don't want to ignore even it they are dot-files
+#
+!.gitignore
+!.mailmap
+
+#
+# Generated include files
+#
+include/config
+include/linux/version.h
+include/generated
+arch/*/include/generated
+
+# stgit generated dirs
+patches-*
+
+# quilt's files
+patches
+series
+
+# cscope files
+cscope.*
+ncscope.*
+
+# gnu global files
+GPATH
+GRTAGS
+GSYMS
+GTAGS
-# Executables
-*.exe
-*.out
-*.app
+*.orig
+*~
+\#*#
View
20 Kconfig
@@ -0,0 +1,20 @@
+menuconfig FB_TFT_SUPPORT
+ tristate "Support for small TFT LCD display modules"
+ depends on FB && SPI && GPIOLIB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select FB_DEFERRED_IO
+
+config FB_ADAFRUIT22
+ tristate "FB driver for the Adafruit 2.2\" LCD display"
+ depends on FB_TFT_SUPPORT
+ help
+ Framebuffer support for the Adafruit 2.2\" LCD display in 9-bit SPI mode.
+
+config FB_SAINSMART18
+ tristate "FB driver for the Sainsmart 1.8\" LCD display"
+ depends on FB_TFT_SUPPORT
+ help
+ Framebuffer support for the Sainsmart 1.8\" LCD display in 8-bit SPI mode.
View
22 Makefile
@@ -0,0 +1,22 @@
+# If KERNELRELEASE is defined, we've been invoked from the
+# kernel build system and can use its language.
+ifneq ($(KERNELRELEASE),)
+obj-m := fbtft.o
+
+obj-m += adafruit22fb.o
+obj-m += sainsmart18fb.o
+
+obj-m += spidevices.o
+
+#obj-$(CONFIG_FB_TFT_SUPPORT) += fbtft.o
+#obj-$(CONFIG_FB_ADAFRUIT22) += adafruit22fb.o
+#obj-$(CONFIG_FB_SAINSMART18) += sainsmart18fb.o
+
+# Otherwise we were called directly from the command
+# line; invoke the kernel build system.
+else
+ KERNELDIR ?= /lib/modules/$(shell uname -r)/build
+ PWD := $(shell pwd)
+default:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
+endif
View
78 README
@@ -0,0 +1,78 @@
+ FBTFT
+=========
+
+Linux Framebuffer drivers for small TFT LCD display modules.
+The drivers uses a module 'fbft' which makes writing these drivers very simple.
+
+Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution.
+
+# uname -r
+3.2.27+
+
+
+SUPPORTED LCD MODULES
+ -----------------------------------------------------------------------------------------
+ | Module | Driver | SPI | Controller | URL |
+ -----------------------------------------------------------------------------------------
+ | Adafruit 2.2" | adafruit22fb | 9 bit | HX8340bn | adafruit.com |
+ | Sainsmart 1.8" | sainsmart18fb | 8 bit | ST7735Rn | www.sainsmart.com |
+ -----------------------------------------------------------------------------------------
+
+ adafruit22fb
+ GPIOS
+ reset (optional if handled by hardware)
+
+ sainsmart18fb
+ GPIOS
+ dc (required)
+ reset (optional if handled by hardware)
+
+
+KERNEL CONFIG
+ The drivers require a kernel with SPI, GPIO, FB, FB_SYS_* and FB_DEFERRED_IO turned on.
+
+ Check with these commands:
+ grep "CONFIG_SPI[= ]\|CONFIG_FB[= ]\|CONFIG_FB_DEFERRED_IO\|CONFIG_FB_SYS" .config
+ grep GPIO .config
+
+
+ Set options with 'make menuconfig'
+
+ CONFIG_SPI=y
+ Device Drivers ---> [*] SPI support ---> <Some controller driver>
+
+ CONFIG_GENERIC_GPIO=y
+ CONFIG_BCM2708_GPIO=y
+ Device Drivers ---> -*- GPIO Support
+
+
+ CONFIG_FB=y
+ Device Drivers ---> Graphics support ---> <*> Support for frame buffer devices
+
+
+ The following options can not be set from menuconfig (can only be set by other config options)
+ Use 'sed' to set them.
+
+ CONFIG_FB_DEFERRED_IO=y
+ sed -i '/CONFIG_FB_DEFERRED_IO/s/# \([A-Z0-9_]*\) is not set/\1=y/' .config
+
+ CONFIG_FB_SYS_FILLRECT=y
+ CONFIG_FB_SYS_COPYAREA=y
+ CONFIG_FB_SYS_IMAGEBLIT=y
+ CONFIG_FB_SYS_FOPS=y
+ sed -i '/CONFIG_FB_SYS/s/# \([A-Z0-9_]*\) is not set/\1=y/' .config
+
+
+TOOLS
+ spidevices
+ This module can add and delete SPI devices to simplify driver testing and development.
+ No need to change the arch/arm/mach-bcm2708/bcm2708.c (on rPi) platform file when testing.
+ Values are hardcoded.
+
+
+KNOWN BUGS
+ 'fbtft' can't be used by more than one driver at a time.
+
+
+SOURCE CODE
+ https://github.com/notro/fbtft/
View
4 README.md
@@ -1,4 +0,0 @@
-fbtft
-=====
-
-Linux Framebuffer drivers for small TFT LCD display modules
View
273 adafruit22fb.c
@@ -0,0 +1,273 @@
+
+
+#define DEBUG
+
+/*
+ * linux/drivers/video/fbtft/adafruit22fb.c -- FB driver for the Adafruit 2.2" LCD display
+ * Based on st7735fb by Matt Porter
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+//#include <linux/fbtft.h>
+#include "fbtft.h"
+
+#define DRVNAME "adafruit22fb"
+#define WIDTH 176
+#define HEIGHT 220
+#define BPP 16
+#define FPS 10
+
+//#define TXBUFLEN -1 // use buffer equal to videomemory*txwordsize
+#define TXBUFLEN 16*PAGE_SIZE
+
+/* Supported display modules */
+#define ST7735_DISPLAY_AF_TFT18 0 /* Adafruit SPI TFT 1.8" */
+
+
+int adafruit22fb_init_display(struct fbtft_par *par)
+{
+ fbtft_write_cmdDef write_cmd = par->fbtftops.write_cmd;
+ fbtft_write_dataDef write_data = par->fbtftops.write_data;
+
+ dev_dbg(par->info->device, "adafruit22fb_init_display()\n");
+
+ par->fbtftops.reset(par);
+
+ /* BTL221722-276L startup sequence, from datasheet */
+
+ /* SETEXTCOM: Set extended command set (C1h)
+ This command is used to set extended command set access enable.
+ Enable: After command (C1h), must write 3 parameters (ffh,83h,40h) by order */
+ write_cmd(par, 0xC1);
+ write_data(par, 0xFF);
+ write_data(par, 0x83);
+ write_data(par, 0x40);
+
+ /* Sleep out
+ This command turns off sleep mode.
+ In this mode the DC/DC converter is enabled, Internal oscillator is started,
+ and panel scanning is started. */
+ write_cmd(par, 0x11);
+
+ mdelay(150);
+
+ /* Undoc'd register? */
+ write_cmd(par, 0xCA);
+ write_data(par, 0x70);
+ write_data(par, 0x00);
+ write_data(par, 0xD9);
+
+ /* SETOSC: Set Internal Oscillator (B0h)
+ This command is used to set internal oscillator related settings */
+ write_cmd(par, 0xB0);
+ write_data(par, 0x01); /* OSC_EN: Enable internal oscillator */
+ write_data(par, 0x11); /* Internal oscillator frequency: 125% x 2.52MHz */
+
+ /* Drive ability setting */
+ write_cmd(par, 0xC9);
+ write_data(par, 0x90);
+ write_data(par, 0x49);
+ write_data(par, 0x10);
+ write_data(par, 0x28);
+ write_data(par, 0x28);
+ write_data(par, 0x10);
+ write_data(par, 0x00);
+ write_data(par, 0x06);
+
+ mdelay(20);
+
+ /* SETGAMMAP: Set "+" polarity Gamma Curve GC0 Related Setting (C2h) */
+ write_cmd(par, 0xC2);
+ write_data(par, 0x60);
+ write_data(par, 0x71);
+ write_data(par, 0x01);
+ write_data(par, 0x0E);
+ write_data(par, 0x05);
+ write_data(par, 0x02);
+ write_data(par, 0x09);
+ write_data(par, 0x31);
+ write_data(par, 0x0A);
+
+ /* SETGAMMAN: Set "-" polarity Gamma Curve GC0 Related Setting (C3h) */
+ write_cmd(par, 0xC3);
+ write_data(par, 0x67);
+ write_data(par, 0x30);
+ write_data(par, 0x61);
+ write_data(par, 0x17);
+ write_data(par, 0x48);
+ write_data(par, 0x07);
+ write_data(par, 0x05);
+ write_data(par, 0x33);
+
+ mdelay(10);
+
+ /* SETPWCTR5: Set Power Control 5(B5h)
+ This command is used to set VCOM Voltage include VCOM Low and VCOM High Voltage */
+ write_cmd(par, 0xB5);
+ write_data(par, 0x35); /* VCOMH 0110101 : 3.925 */
+ write_data(par, 0x20); /* VCOML 0100000 : -1.700 */
+ write_data(par, 0x45); /* 45h=69 VCOMH: "VMH" + 5d VCOML: "VMH" + 5d */
+
+ /* SETPWCTR4: Set Power Control 4(B4h)
+ VRH[4:0]: Specify the VREG1 voltage adjusting. VREG1 voltage is for gamma voltage setting.
+ BT[2:0]: Switch the output factor of step-up circuit 2 for VGH and VGL voltage generation. */
+ write_cmd(par, 0xB4);
+ write_data(par, 0x33);
+ write_data(par, 0x25);
+ write_data(par, 0x4C);
+
+ mdelay(10);
+
+ /* Interface Pixel Format (3Ah)
+ This command is used to define the format of RGB picture data,
+ which is to be transfer via the system and RGB interface. */
+ write_cmd(par, 0x3A);
+ write_data(par, 0x05); /* RGB interface: 16 Bit/Pixel */
+
+ /* Display on (29h)
+ This command is used to recover from DISPLAY OFF mode.
+ Output from the Frame Memory is enabled. */
+ write_cmd(par, 0x29);
+
+ mdelay(10);
+
+ return 0;
+}
+
+
+struct fbtft_display adafruit22_display = {
+ .width = WIDTH,
+ .height = HEIGHT,
+ .bpp = BPP,
+ .fps = FPS,
+ .txbuflen = TXBUFLEN,
+ .txwordsize = 2,
+ .txdatabitmask = 0x0100,
+};
+
+
+static int __devinit adafruit22fb_probe(struct spi_device *spi)
+{
+ int chip = spi_get_device_id(spi)->driver_data;
+ struct fb_info *info;
+ struct fbtft_par *par;
+ int ret;
+
+//--------------------------------------------------------------------------------
+
+//[297346.179960] hx8340fb spi0.0: spi->dev.platform_data bf1e48d0
+//dev_err(&spi->dev, "spi->dev.platform_data %p\n", spi->dev.platform_data);
+
+
+//spi->max_speed_hz = 32000000;
+//spi->mode = SPI_CPHA | SPI_CPOL; // or #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
+//--------------------------------------------------------------------------------
+
+
+ dev_dbg(&spi->dev, "adafruit22fb_probe()\n");
+
+ if (chip != ST7735_DISPLAY_AF_TFT18) {
+ dev_err(&spi->dev, "only the %s device is supported\n",
+ to_spi_driver(spi->dev.driver)->id_table->name);
+ return -EINVAL;
+ }
+
+ if (!(spi->mode == SPI_MODE_3)) {
+ dev_err(&spi->dev, "SPI mode not supported (must be SPI_MODE_3)\n");
+ return -EINVAL;
+ }
+
+ spi->bits_per_word=9;
+ ret = spi->master->setup(spi);
+ if (ret)
+ dev_err(&spi->dev, "spi->master->setup(spi) failed, returned %d\n", ret);
+
+ info = fbtft_framebuffer_alloc(&adafruit22_display, &spi->dev);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ par->spi = spi;
+ par->fbtftops.init_display = adafruit22fb_init_display;
+
+ ret = fbtft_register_framebuffer(info);
+ if (ret < 0)
+ goto fbreg_fail;
+
+ return 0;
+
+fbreg_fail:
+ fbtft_framebuffer_release(info);
+
+ return ret;
+}
+
+static int __devexit adafruit22fb_remove(struct spi_device *spi)
+{
+ struct fb_info *info = spi_get_drvdata(spi);
+
+ if (info) {
+ fbtft_unregister_framebuffer(info);
+ fbtft_framebuffer_release(info);
+ }
+
+ return 0;
+}
+
+static const struct spi_device_id adafruit22fb_ids[] = {
+ { DRVNAME, ST7735_DISPLAY_AF_TFT18 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(spi, adafruit22fb_ids);
+
+static struct spi_driver adafruit22fb_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+ .id_table = adafruit22fb_ids,
+ .probe = adafruit22fb_probe,
+ .remove = __devexit_p(adafruit22fb_remove),
+};
+
+static int __init adafruit22fb_init(void)
+{
+ pr_debug("\n\n"DRVNAME" - init\n");
+ return spi_register_driver(&adafruit22fb_driver);
+}
+
+static void __exit adafruit22fb_exit(void)
+{
+ pr_debug(DRVNAME" - exit\n");
+ spi_unregister_driver(&adafruit22fb_driver);
+}
+
+/* ------------------------------------------------------------------------- */
+
+module_init(adafruit22fb_init);
+module_exit(adafruit22fb_exit);
+
+MODULE_DESCRIPTION("FB driver for the Adafruit 2.2\" LCD display");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
View
756 fbtft.c
@@ -0,0 +1,756 @@
+#define DEBUG
+
+/*
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+//#include <linux/fbtft.h>
+#include "fbtft.h"
+
+
+
+unsigned long fbtft_request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio)
+{
+ if (strcasecmp(gpio->name, "reset") == 0) {
+ par->rst = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ }
+ else if (strcasecmp(gpio->name, "dc") == 0) {
+ par->dc = gpio->gpio;
+ return GPIOF_OUT_INIT_LOW;
+ }
+ else if (strcasecmp(gpio->name, "bl") == 0) {
+ par->bl = gpio->gpio;
+ return GPIOF_OUT_INIT_HIGH;
+ }
+
+ return FBTFT_GPIO_NO_MATCH;
+}
+
+int fbtft_request_gpios(struct fbtft_par *par)
+{
+ struct fbtft_platform_data *pdata = par->pdata;
+ const struct fbtft_gpio *gpio;
+ unsigned long flags;
+ int ret;
+
+ if (pdata && pdata->gpios) {
+ gpio = pdata->gpios;
+ while (gpio->name[0]) {
+
+
+ flags = par->fbtftops.request_gpios_match(par, gpio);
+
+ if (flags != FBTFT_GPIO_NO_MATCH) {
+ ret = gpio_request_one(gpio->gpio, flags, par->info->device->driver->name);
+ if (ret < 0) {
+ dev_err(par->info->device, "fbtft_request_gpios: could not acquire '%s' GPIO%d\n", gpio->name, gpio->gpio);
+ return ret;
+ }
+ dev_dbg(par->info->device, "fbtft_request_gpios: acquired '%s' GPIO%d\n", gpio->name, gpio->gpio);
+ }
+ gpio++;
+ }
+ }
+
+ return 0;
+}
+
+void fbtft_free_gpios(struct fbtft_par *par)
+{
+ struct spi_device *spi = par->spi;
+ struct fbtft_platform_data *pdata = spi->dev.platform_data;
+ const struct fbtft_gpio *gpio;
+
+ if (pdata && pdata->gpios) {
+ gpio = pdata->gpios;
+ while (gpio->name[0]) {
+ gpio_free(gpio->gpio);
+ gpio++;
+ }
+ }
+}
+
+
+
+int fbtft_write(struct fbtft_par *par, void *buf, size_t len)
+{
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ .speed_hz = par->throttle_speed,
+ };
+ struct spi_message m;
+
+//dev_dbg(par->info->device, "fbtft_write: buf=%p, len=%d%s\n", buf, len, par->throttle_speed ? ", throttled" : "");
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ return spi_sync(par->spi, &m);
+}
+
+/*
+int fbtft_write(struct fbtft_par *par, void *buf, size_t len)
+{
+// dev_dbg(par->info->device, " fbtft_write: len=%d\n", len);
+ return spi_write(par->spi, buf, len);
+}
+*/
+
+void fbtft_write_data_command(struct fbtft_par *par, unsigned dc, u8 val)
+{
+ size_t len = 1;
+ void *tx = &val;
+ u16 tx16 = val;
+ u32 tx32 = val;
+ int ret;
+
+ dev_dbg(par->info->device, "fbtft_write_data_command: dc=%d, val=0x%X\n", dc, val);
+
+ if (par->dc != -1)
+ gpio_set_value(par->dc, dc);
+
+ if (par->txbuf.buf) {
+ len = par->txbuf.wordsize;
+ if (len == 2) {
+ if (dc)
+ tx16 |= par->txbuf.databitmask;
+ tx = &tx16;
+ } else if (len == 4) {
+ if (dc)
+ tx32 |= par->txbuf.databitmask;
+ tx = &tx32;
+ }
+ }
+
+ ret = par->fbtftops.write(par, tx, len);
+ if (ret < 0)
+ dev_err(par->info->device, "fbtft_write_data_command: dc=%d, val=0x%X, failed with status %d\n", dc, val, ret);
+}
+
+void fbtft_write_data(struct fbtft_par *par, u8 data)
+{
+ fbtft_write_data_command(par, 1, data);
+}
+
+void fbtft_write_cmd(struct fbtft_par *par, u8 data)
+{
+ fbtft_write_data_command(par, 0, data);
+}
+
+int fbtft_write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u8 *vmem8 = par->info->screen_base + offset;
+ u8 *txbuf8 = par->txbuf.buf;
+ u16 *txbuf16 = par->txbuf.buf;
+ u32 *txbuf32 = par->txbuf.buf;
+ size_t remain = len;
+ size_t to_copy;
+ size_t tx_array_size;
+ int i;
+ int ret = 0;
+
+ dev_dbg(par->info->device, "fbtft_write_vmem: offset=%d, len=%d\n", offset, len);
+
+ if (par->dc != -1)
+ gpio_set_value(par->dc, 1);
+
+ // non buffered write
+ if (!par->txbuf.buf)
+ return par->fbtftops.write(par, vmem8, len);
+
+ // sanity checks
+ if (!(par->txbuf.wordsize == 1 || par->txbuf.wordsize == 2 || par->txbuf.wordsize == 4)) {
+ dev_err(par->info->device, "fbtft_write_vmem: txbuf.wordsize=%d not supported, must be 1, 2 or 4\n", par->txbuf.wordsize);
+ return -1;
+ }
+
+ // buffered write
+ tx_array_size = par->txbuf.len / par->txbuf.wordsize;
+
+ dev_dbg(par->info->device, " tx_array_size=%d, wordsize=%d\n", tx_array_size, par->txbuf.wordsize);
+
+ while (remain) {
+ to_copy = remain > tx_array_size ? tx_array_size : remain;
+ dev_dbg(par->info->device, " to_copy=%d, remain=%d\n", to_copy, remain - to_copy);
+
+ if (par->txbuf.wordsize == 1) {
+#ifdef __LITTLE_ENDIAN
+ for (i=0;i<to_copy;i+=2) {
+ txbuf8[i] = vmem8[i+1];
+ txbuf8[i+1] = vmem8[i];
+ }
+#else
+ for (i=0;i<to_copy;i++)
+ txbuf8[i] = vmem8[i];
+#endif
+ }
+ else if (par->txbuf.wordsize == 2) {
+#ifdef __LITTLE_ENDIAN
+ for (i=0;i<to_copy;i+=2) {
+ txbuf16[i] = par->txbuf.databitmask | vmem8[i+1];
+ txbuf16[i+1] = par->txbuf.databitmask | vmem8[i];
+ }
+#else
+ for (i=0;i<to_copy;i++)
+ txbuf16[i] = par->txbuf.databitmask | vmem8[i];
+#endif
+ }
+ else if (par->txbuf.wordsize == 4) {
+#ifdef __LITTLE_ENDIAN
+ for (i=0;i<to_copy;i+=2) {
+ txbuf32[i] = par->txbuf.databitmask | vmem8[i+1];
+ txbuf32[i+1] = par->txbuf.databitmask | vmem8[i];
+ }
+#else
+ for (i=0;i<to_copy;i++)
+ txbuf32[i] = par->txbuf.databitmask | vmem8[i];
+#endif
+ }
+ vmem8 = vmem8 + to_copy;
+ ret = par->fbtftops.write(par, par->txbuf.buf, to_copy*par->txbuf.wordsize);
+ if (ret < 0)
+ return ret;
+ remain -= to_copy;
+ }
+
+ return ret;
+}
+
+void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
+{
+ dev_dbg(par->info->device, "fbtft_set_addr_win(%d, %d, %d, %d)\n", xs, ys, xe, ye);
+
+ par->fbtftops.write_cmd(par, FBTFT_CASET);
+ par->fbtftops.write_data(par, 0x00);
+ par->fbtftops.write_data(par, xs);
+ par->fbtftops.write_data(par, 0x00);
+ par->fbtftops.write_data(par, xe);
+
+ par->fbtftops.write_cmd(par, FBTFT_RASET);
+ par->fbtftops.write_data(par, 0x00);
+ par->fbtftops.write_data(par, ys);
+ par->fbtftops.write_data(par, 0x00);
+ par->fbtftops.write_data(par, ye);
+
+ par->fbtftops.write_cmd(par, FBTFT_RAMWR);
+}
+
+
+void fbtft_reset(struct fbtft_par *par)
+{
+ if (par->rst == -1)
+ return;
+ dev_dbg(par->info->device, "fbtft_reset()\n");
+ gpio_set_value(par->rst, 0);
+ udelay(20);
+ gpio_set_value(par->rst, 1);
+ mdelay(120);
+}
+
+
+void fbtft_update_display(struct fbtft_par *par)
+{
+ size_t offset, len;
+ int ret = 0;
+
+ // Sanity checks
+ if (par->dirty_low > par->dirty_high) {
+ dev_warn(par->info->device,
+ "update_display: dirty_low=%d is larger than dirty_high=%d. Shouldn't happen, will do full display update\n",
+ par->dirty_low, par->dirty_high);
+ par->dirty_low = 0;
+ par->dirty_high = par->info->var.yres - 1;
+ }
+ if (par->dirty_low > par->info->var.yres - 1 || par->dirty_high > par->info->var.yres - 1) {
+ dev_warn(par->info->device,
+ "update_display: dirty_low=%d or dirty_high=%d larger than max=%d. Shouldn't happen, will do full display update\n",
+ par->dirty_low, par->dirty_high, par->info->var.yres - 1);
+ par->dirty_low = 0;
+ par->dirty_high = par->info->var.yres - 1;
+ }
+
+ dev_dbg(par->info->device, "update_display dirty_low=%d dirty_high=%d\n", par->dirty_low, par->dirty_high);
+
+ // set display area where update goes
+ par->fbtftops.set_addr_win(par, 0, par->dirty_low, par->info->var.xres-1, par->dirty_high);
+
+ offset = par->dirty_low * par->info->fix.line_length;
+ len = (par->dirty_high - par->dirty_low + 1) * par->info->fix.line_length;
+
+ ret = par->fbtftops.write_vmem(par, offset, len);
+ if (ret < 0)
+ pr_err("%s: spi_write failed to update display buffer\n",
+ par->info->fix.id);
+
+ // set display line markers as clean
+ par->dirty_low = par->info->var.yres - 1;
+ par->dirty_high = 0;
+
+ dev_dbg(par->info->device, "\n");
+}
+
+
+void fbtft_mkdirty(struct fb_info *info, int y, int height)
+{
+ struct fbtft_par *par = info->par;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ // special case, needed ?
+ if (y == -1) {
+ y = 0;
+ height = info->var.yres - 1;
+ }
+
+ // Mark display lines/area as dirty
+ if (y < par->dirty_low)
+ par->dirty_low = y;
+ if (y + height - 1 > par->dirty_high)
+ par->dirty_high = y + height - 1;
+
+ // Schedule deferred_io to update display (no-op if already on queue)
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+}
+
+// drivers/video/broadsheetfb.c
+// drivers/video/xen-fbfront.c
+void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ struct fbtft_par *par = info->par;
+ struct page *page;
+ unsigned long index;
+ unsigned y_low=0, y_high=0;
+ int count = 0;
+
+ // Mark display lines as dirty
+ list_for_each_entry(page, pagelist, lru) {
+ count++;
+ index = page->index << PAGE_SHIFT;
+ y_low = index / info->fix.line_length;
+ y_high = (index + PAGE_SIZE - 1) / info->fix.line_length;
+dev_dbg(info->device, "page->index=%lu y_low=%d y_high=%d\n", page->index, y_low, y_high);
+ if (y_high > info->var.yres - 1)
+ y_high = info->var.yres - 1;
+ if (y_low < par->dirty_low)
+ par->dirty_low = y_low;
+ if (y_high > par->dirty_high)
+ par->dirty_high = y_high;
+ }
+
+//dev_err(info->device, "deferred_io count=%d\n", count);
+
+
+ par->fbtftops.update_display(info->par);
+}
+
+
+void fbtft_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct fbtft_par *par = info->par;
+
+ dev_dbg(info->dev, "fillrect dx=%d, dy=%d, width=%d, height=%d\n", rect->dx, rect->dy, rect->width, rect->height);
+ sys_fillrect(info, rect);
+
+ par->fbtftops.mkdirty(info, rect->dy, rect->height);
+}
+
+void fbtft_fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct fbtft_par *par = info->par;
+
+ dev_dbg(info->dev, "copyarea dx=%d, dy=%d, width=%d, height=%d\n", area->dx, area->dy, area->width, area->height);
+ sys_copyarea(info, area);
+
+ par->fbtftops.mkdirty(info, area->dy, area->height);
+}
+
+void fbtft_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct fbtft_par *par = info->par;
+
+ dev_dbg(info->dev, "imageblit dx=%d, dy=%d, width=%d, height=%d\n", image->dx, image->dy, image->width, image->height);
+ sys_imageblit(info, image);
+
+ par->fbtftops.mkdirty(info, image->dy, image->height);
+}
+
+ssize_t fbtft_fb_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct fbtft_par *par = info->par;
+ ssize_t res;
+
+ dev_dbg(info->dev, "write count=%zd, ppos=%llu)\n", count, *ppos);
+ res = fb_sys_write(info, buf, count, ppos);
+
+ // TODO: only mark changed area
+ // update all for now
+ par->fbtftops.mkdirty(info, -1, 0);
+
+ return res;
+}
+
+// from pxafb.c
+unsigned int chan_to_field(unsigned chan, struct fb_bitfield *bf)
+{
+ chan &= 0xffff;
+ chan >>= 16 - bf->length;
+ return chan << bf->offset;
+}
+
+int fbtft_fb_setcolreg(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *info)
+{
+ unsigned val;
+ int ret = 1;
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ if (regno < 16) {
+ u32 *pal = info->pseudo_palette;
+
+ val = chan_to_field(red, &info->var.red);
+ val |= chan_to_field(green, &info->var.green);
+ val |= chan_to_field(blue, &info->var.blue);
+
+ pal[regno] = val;
+ ret = 0;
+ }
+ break;
+
+ }
+ return ret;
+}
+
+// http://www.embeddedlinux.org.cn/EssentialLinuxDeviceDrivers/final/ch12lev1sec5.html
+int fbtft_fb_blank(int blank, struct fb_info *info)
+{
+ struct fbtft_par *par = info->par;
+ int ret = -1;
+
+ if (!par->fbtftops.setpower)
+ return -1;
+
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ case FB_BLANK_VSYNC_SUSPEND:
+ case FB_BLANK_HSYNC_SUSPEND:
+ case FB_BLANK_NORMAL:
+ ret = par->fbtftops.setpower(par, 1);
+ break;
+ case FB_BLANK_UNBLANK:
+ ret = par->fbtftops.setpower(par, 0);
+ break;
+ }
+ return ret;
+}
+
+
+
+/**
+ * fbtft_framebuffer_alloc - creates a new frame buffer info structure
+ *
+ * @display: pointer to structure describing the display
+ * @dev: pointer to the device for this fb, this can be NULL
+ *
+ * Creates a new frame buffer info structure.
+ *
+ * Also creates and populates the following structures:
+ * info->fbops
+ * info->fbdefio
+ * info->pseudo_palette
+ * par->fbtftops
+ * par->txbuf
+ *
+ * Returns the new structure, or NULL if an error occurred.
+ *
+ */
+//struct fb_info *fbtft_framebuffer_alloc(unsigned width, unsigned height, unsigned bpp, unsigned fps, int txbufsize, struct device *dev)
+struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, struct device *dev)
+{
+ struct fb_info *info;
+ struct fbtft_par *par;
+ struct fb_ops *fbops = NULL;
+ struct fb_deferred_io *fbdefio = NULL;
+ u8 *vmem = NULL;
+ void *txbuf = NULL;
+ unsigned txwordsize = display->txwordsize;
+ int vmem_size = display->width*display->height*display->bpp/8;
+
+ // sanity checks
+ if (display->bpp != 16) {
+ dev_err(dev, "only 16bpp supported.\n");
+ return NULL;
+ }
+ if (!txwordsize)
+ txwordsize = 1;
+
+ vmem = vzalloc(vmem_size);
+ if (!vmem)
+ goto alloc_fail;
+
+ fbops = kzalloc(sizeof(struct fb_ops), GFP_KERNEL);
+ if (!fbops)
+ goto alloc_fail;
+
+ fbdefio = kzalloc(sizeof(struct fb_deferred_io), GFP_KERNEL);
+ if (!fbdefio)
+ goto alloc_fail;
+
+ info = framebuffer_alloc(sizeof(struct fbtft_par), dev);
+ if (!info)
+ goto alloc_fail;
+
+ info->screen_base = (u8 __force __iomem *)vmem;
+ info->fbops = fbops;
+ info->fbdefio = fbdefio;
+
+ fbops->owner = dev->driver->owner;
+ fbops->fb_read = fb_sys_read;
+ fbops->fb_write = fbtft_fb_write;
+ fbops->fb_fillrect = fbtft_fb_fillrect;
+ fbops->fb_copyarea = fbtft_fb_copyarea;
+ fbops->fb_imageblit = fbtft_fb_imageblit;
+ fbops->fb_setcolreg = fbtft_fb_setcolreg;
+
+ fbdefio->delay = HZ/display->fps;
+ fbdefio->deferred_io = fbtft_deferred_io;
+ fb_deferred_io_init(info);
+
+ strncpy(info->fix.id, dev->driver->name, 16);
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.xpanstep = 0;
+ info->fix.ypanstep = 0;
+ info->fix.ywrapstep = 0;
+ info->fix.line_length = display->width*display->bpp/8;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_len = vmem_size;
+
+ info->var.xres = display->width;
+ info->var.yres = display->height;
+ info->var.xres_virtual = display->width;
+ info->var.yres_virtual = display->height;
+ info->var.bits_per_pixel = display->bpp;
+ info->var.nonstd = 1;
+
+ // RGB565
+ info->var.red.offset = 11;
+ info->var.red.length = 5;
+ info->var.green.offset = 5;
+ info->var.green.length = 6;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 5;
+ info->var.transp.offset = 0;
+ info->var.transp.length = 0;
+
+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
+
+ par = info->par;
+ par->info = info;
+ par->display = display;
+ par->pdata = dev->platform_data;
+ par->rst = -1;
+ par->dc = -1;
+ par->bl = -1;
+ // Set display line markers as dirty for all. Ensures first update to update all of the display.
+ par->dirty_low = 0;
+ par->dirty_high = par->info->var.yres - 1;
+
+ info->pseudo_palette = par->pseudo_palette;
+
+ // Transmit buffer
+ par->txbuf.wordsize = txwordsize;
+ if (display->txbuflen == -1)
+ par->txbuf.len = vmem_size * par->txbuf.wordsize;
+ else if (display->txbuflen)
+ par->txbuf.len = display->txbuflen;
+ else
+ par->txbuf.len = 0;
+
+ par->txbuf.databitmask = display->txdatabitmask;
+
+#ifdef __LITTLE_ENDIAN
+ // need buffer for byteswapping
+ if (!par->txbuf.len)
+ par->txbuf.len = PAGE_SIZE;
+#endif
+
+ if (par->txbuf.len) {
+ txbuf = vzalloc(par->txbuf.len);
+ if (!txbuf)
+ goto alloc_fail;
+ par->txbuf.buf = txbuf;
+ }
+
+ // default fbtft operations
+ par->fbtftops.write = fbtft_write;
+ par->fbtftops.write_data = fbtft_write_data;
+ par->fbtftops.write_cmd = fbtft_write_cmd;
+ par->fbtftops.write_vmem = fbtft_write_vmem;
+ par->fbtftops.set_addr_win = fbtft_set_addr_win;
+ par->fbtftops.reset = fbtft_reset;
+ par->fbtftops.mkdirty = fbtft_mkdirty;
+ par->fbtftops.update_display = fbtft_update_display;
+
+ par->fbtftops.request_gpios_match = fbtft_request_gpios_match;
+ par->fbtftops.request_gpios = fbtft_request_gpios;
+ par->fbtftops.free_gpios = fbtft_free_gpios;
+
+ return info;
+
+alloc_fail:
+ if (vmem)
+ vfree(vmem);
+ if (txbuf)
+ vfree(txbuf);
+ if (fbops)
+ kfree(fbops);
+ if (fbdefio)
+ kfree(fbdefio);
+
+ return NULL;
+}
+EXPORT_SYMBOL(fbtft_framebuffer_alloc);
+
+/**
+ * fbtft_framebuffer_release - frees up all memory used by the framebuffer
+ *
+ * @info: frame buffer info structure
+ *
+ */
+void fbtft_framebuffer_release(struct fb_info *info)
+{
+ struct fbtft_par *par = info->par;
+
+ fb_deferred_io_cleanup(info);
+ vfree(info->screen_base);
+ if (par->txbuf.buf)
+ vfree(par->txbuf.buf);
+ kfree(info->fbops);
+ kfree(info->fbdefio);
+ framebuffer_release(info);
+ gpio_free(par->rst);
+}
+EXPORT_SYMBOL(fbtft_framebuffer_release);
+
+/**
+ * fbtft_register_framebuffer - registers a tft frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Sets SPI driverdata if needed
+ * Requests needed gpios.
+ * Initializes display
+ * Updates display.
+ * Registers a frame buffer device @fb_info.
+ *
+ * Returns negative errno on error, or zero for success.
+ *
+ */
+int fbtft_register_framebuffer(struct fb_info *fb_info)
+{
+ int ret;
+ char text1[50] = "";
+ char text2[50] = "";
+ char text3[50] = "";
+ char text4[50] = "";
+ struct fbtft_par *par = fb_info->par;
+ struct spi_device *spi = par->spi;
+
+ // sanity check
+ if (!par->fbtftops.init_display) {
+ dev_err(fb_info->device, "missing init_display()\n");
+ return -EINVAL;
+ }
+
+ if (spi)
+ spi_set_drvdata(spi, fb_info);
+
+ ret = par->fbtftops.request_gpios(par);
+ if (ret < 0)
+ goto reg_fail;
+
+ ret = par->fbtftops.init_display(par);
+ if (ret < 0)
+ goto reg_fail;
+
+ par->fbtftops.update_display(par);
+
+ ret = register_framebuffer(fb_info);
+ if (ret < 0)
+ goto reg_fail;
+
+ // [Tue Jan 8 19:36:41 2013] graphics fb1: hx8340fb frame buffer, 75 KiB video memory, 151 KiB buffer memory, spi0.0 at 32 MHz, GPIO25 for reset
+ if (par->txbuf.buf)
+ sprintf(text1, ", %d KiB buffer memory", par->txbuf.len >> 10);
+ if (spi)
+ sprintf(text2, ", spi%d.%d at %d MHz", spi->master->bus_num, spi->chip_select, spi->max_speed_hz/1000000);
+ if (par->rst != -1)
+ sprintf(text3, ", GPIO%d for reset", par->rst);
+ if (par->dc != -1)
+ sprintf(text4, ", GPIO%d for D/C", par->dc);
+ dev_info(fb_info->dev, "%s frame buffer, %d KiB video memory%s%s%s%s\n",
+ fb_info->fix.id, fb_info->fix.smem_len >> 10, text1, text2, text3, text4);
+
+ return 0;
+
+reg_fail:
+ spi_set_drvdata(spi, NULL);
+ par->fbtftops.free_gpios(par);
+
+ return ret;
+}
+EXPORT_SYMBOL(fbtft_register_framebuffer);
+
+/**
+ * fbtft_unregister_framebuffer - releases a tft frame buffer device
+ * @fb_info: frame buffer info structure
+ *
+ * Frees SPI driverdata if needed
+ * Frees gpios.
+ * Unregisters frame buffer device.
+ *
+ */
+int fbtft_unregister_framebuffer(struct fb_info *fb_info)
+{
+ struct fbtft_par *par = fb_info->par;
+ struct spi_device *spi = par->spi;
+
+ if (spi)
+ spi_set_drvdata(spi, NULL);
+ par->fbtftops.free_gpios(par);
+ return unregister_framebuffer(fb_info);
+}
+EXPORT_SYMBOL(fbtft_unregister_framebuffer);
+
+
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
View
116 fbtft.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __LINUX_FBTFT_H
+#define __LINUX_FBTFT_H
+
+#include <linux/fb.h>
+
+
+#define FBTFT_MAX_BITS 32
+
+#define FBTFT_NOP 0x00
+#define FBTFT_SWRESET 0x01
+#define FBTFT_RDDID 0x04
+#define FBTFT_RDDST 0x09
+#define FBTFT_CASET 0x2A
+#define FBTFT_RASET 0x2B
+#define FBTFT_RAMWR 0x2C
+
+
+#define FBTFT_GPIO_NO_MATCH 0xFFFF
+#define FBTFT_GPIO_NAME_SIZE 32
+
+struct fbtft_gpio {
+ char name[FBTFT_GPIO_NAME_SIZE];
+ unsigned gpio;
+};
+
+#define FBTFT_MAX_GPIOS 32
+struct fbtft_platform_data {
+ const struct fbtft_gpio *gpios;
+ int rst_gpio;
+ int dc_gpio;
+ void *extra;
+};
+
+
+struct fbtft_par;
+
+typedef void (*fbtft_write_dataDef)(struct fbtft_par *par, u8 data);
+typedef void (*fbtft_write_cmdDef)(struct fbtft_par *par, u8 data);
+
+struct fbtft_ops {
+ fbtft_write_dataDef write_data;
+ fbtft_write_cmdDef write_cmd;
+
+ int (*write)(struct fbtft_par *par, void *buf, size_t len);
+ int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len);
+
+ void (*set_addr_win)(struct fbtft_par *par, int xs, int ys, int xe, int ye);
+ void (*reset)(struct fbtft_par *par);
+ void (*mkdirty)(struct fb_info *info, int from, int to);
+ void (*update_display)(struct fbtft_par *par);
+ int (*init_display)(struct fbtft_par *par);
+ int (*setpower)(struct fbtft_par *par, int on);
+
+ unsigned long (*request_gpios_match)(struct fbtft_par *par, const struct fbtft_gpio *gpio);
+ int (*request_gpios)(struct fbtft_par *par);
+ void (*free_gpios)(struct fbtft_par *par);
+
+};
+
+struct fbtft_display {
+ unsigned width;
+ unsigned height;
+ unsigned bpp;
+ unsigned fps;
+ int txbuflen;
+ unsigned txwordsize;
+ unsigned txdatabitmask;
+};
+
+struct fbtft_par {
+ struct fbtft_display *display;
+ struct spi_device *spi;
+ struct fb_info *info;
+ struct fbtft_platform_data *pdata;
+ u16 *ssbuf;
+ u32 pseudo_palette[16];
+ struct {
+ void *buf;
+ size_t len;
+ unsigned wordsize;
+ unsigned databitmask;
+ } txbuf;
+ struct fbtft_ops fbtftops;
+ unsigned dirty_low;
+ unsigned dirty_high;
+ u32 throttle_speed;
+ int rst;
+ int dc;
+ int bl;
+ void *extra;
+};
+
+extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, struct device *dev);
+extern void fbtft_framebuffer_release(struct fb_info *info);
+extern int fbtft_register_framebuffer(struct fb_info *fb_info);
+extern int fbtft_unregister_framebuffer(struct fb_info *fb_info);
+
+#endif /* __LINUX_FBTFT_H */
View
426 sainsmart18fb.c
@@ -0,0 +1,426 @@
+
+
+#define DEBUG
+
+/*
+ * linux/drivers/video/fbtft/sainsmart18fb.c -- FB driver for the Sainsmart 1.8" LCD display
+ * Based on st7735fb by Matt Porter, Neil Greatorex and Kamal Mostafa
+ *
+ * This display module want the color as BGR565
+ * Some programs like mplayer writes RGB565 regardless of what is said in info->var.[color].{offset,length}.
+ * So conversion from RGB565 to BGR565 has to be done.
+ *
+ *
+ * Copyright (C) 2013 Noralf Tronnes
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+//#include <linux/fbtft.h>
+#include "fbtft.h"
+
+#define DRVNAME "sainsmart18fb"
+#define WIDTH 128
+#define HEIGHT 160
+#define BPP 16
+#define FPS 10
+#define TXBUFLEN 4*PAGE_SIZE
+
+/* Supported display modules */
+#define ST7735_DISPLAY_AF_TFT18 0 /* Adafruit SPI TFT 1.8" */
+
+// Reading RDDID
+// http://rossum.posterous.com/screen-play-lots-of-other-screens-for-microco
+
+
+// ftp://imall.iteadstudio.com/IM120419001_ITDB02_1.8SP/DS_ST7735.pdf
+// https://github.com/johnmccombs/arduino-libraries/blob/master/ST7735/ST7735.cpp
+
+int sainsmart18fb_init_display(struct fbtft_par *par)
+{
+ fbtft_write_cmdDef write_cmd = par->fbtftops.write_cmd;
+ fbtft_write_dataDef write_data = par->fbtftops.write_data;
+
+ dev_dbg(par->info->device, "sainsmart18fb_init_display()\n");
+
+ par->fbtftops.reset(par);
+
+ par->throttle_speed = 2000000;
+
+ // SWRESET - Software reset
+ write_cmd(par, 0x01);
+ mdelay(150);
+
+ // SLPOUT - Sleep out & booster on
+ write_cmd(par, 0x11);
+ mdelay(500);
+
+ // FRMCTR1 - frame rate control - normal mode
+ write_cmd(par, 0xB1);
+ write_data(par, 0x01); // frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D)
+ write_data(par, 0x2C);
+ write_data(par, 0x2D);
+
+ // FRMCTR2 - frame rate control - idle mode
+ write_cmd(par, 0xB2);
+ write_data(par, 0x01); // frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D)
+ write_data(par, 0x2C);
+ write_data(par, 0x2D);
+
+ // FRMCTR1 - frame rate control - partial mode
+ write_cmd(par, 0xB3);
+ write_data(par, 0x01); // dot inversion mode
+ write_data(par, 0x2C);
+ write_data(par, 0x2D);
+ write_data(par, 0x01); // line inversion mode
+ write_data(par, 0x2C);
+ write_data(par, 0x2D);
+
+ // INVCTR - // display inversion control
+ write_cmd(par, 0xB4);
+ write_data(par, 0x07); // no inversion
+
+ // PWCTR1 - Power Control
+ write_cmd(par, 0xC0);
+ write_data(par, 0xA2);
+ write_data(par, 0x02); // -4.6V
+ write_data(par, 0x84); // AUTO mode
+
+ // PWCTR2 - Power Control
+ write_cmd(par, 0xC1);
+ write_data(par, 0xC5); // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
+
+ // PWCTR3 - Power Control
+ write_cmd(par, 0xC2);
+ write_data(par, 0x0A); // Opamp current small
+ write_data(par, 0x00); // Boost frequency
+
+ // PWCTR4 - Power Control
+ write_cmd(par, 0xC3);
+ write_data(par, 0x8A); // BCLK/2, Opamp current small & Medium low
+ write_data(par, 0x2A);
+
+ // PWCTR5 - Power Control
+ write_cmd(par, 0xC4);
+ write_data(par, 0x8A);
+ write_data(par, 0xEE);
+
+ // VMCTR1 - Power Control
+ write_cmd(par, 0xC5);
+ write_data(par, 0x0E);
+
+ // INVOFF - Display inversion off
+ write_cmd(par, 0x20);
+
+ // MADCTL - Memory data access control
+ write_cmd(par, 0x36);
+ write_data(par, 0xC8); // row address/col address, bottom to top refresh
+
+ // COLMOD - Interface pixel format
+ write_cmd(par, 0x3A);
+ write_data(par, 0x05);
+
+
+/*
+ // GMCTRP1
+ write_cmd(par, 0xE0);
+ write_data(par, 0x02);
+ write_data(par, 0x1c);
+ write_data(par, 0x07);
+ write_data(par, 0x12);
+ write_data(par, 0x37);
+ write_data(par, 0x32);
+ write_data(par, 0x29);
+ write_data(par, 0x2d);
+ write_data(par, 0x29);
+ write_data(par, 0x25);
+ write_data(par, 0x2b);
+ write_data(par, 0x39);
+ write_data(par, 0x00);
+ write_data(par, 0x01);
+ write_data(par, 0x03);
+ write_data(par, 0x10);
+
+ // GMCTRN1
+ write_cmd(par, 0xE1);
+ write_data(par, 0x03);
+ write_data(par, 0x1d);
+ write_data(par, 0x07);
+ write_data(par, 0x06);
+ write_data(par, 0x2e);
+ write_data(par, 0x2c);
+ write_data(par, 0x29);
+ write_data(par, 0x2d);
+ write_data(par, 0x2e);
+ write_data(par, 0x2e);
+ write_data(par, 0x37);
+ write_data(par, 0x3f);
+ write_data(par, 0x00);
+ write_data(par, 0x00);
+ write_data(par, 0x02);
+ write_data(par, 0x10);
+*/
+
+
+ // GMCTRP1 - Gamma control
+ write_cmd(par, 0xE0);
+ write_data(par, 0x0f);
+ write_data(par, 0x1a);
+ write_data(par, 0x0f);
+ write_data(par, 0x18);
+ write_data(par, 0x2f);
+ write_data(par, 0x28);
+ write_data(par, 0x20);
+ write_data(par, 0x22);
+ write_data(par, 0x1f);
+ write_data(par, 0x1b);
+ write_data(par, 0x23);
+ write_data(par, 0x37);
+ write_data(par, 0x00);
+ write_data(par, 0x07);
+ write_data(par, 0x02);
+ write_data(par, 0x10);
+
+ // GMCTRN1 - Gamma control
+ write_cmd(par, 0xE1);
+ write_data(par, 0x0f);
+ write_data(par, 0x1b);
+ write_data(par, 0x0f);
+ write_data(par, 0x17);
+ write_data(par, 0x33);
+ write_data(par, 0x2c);
+ write_data(par, 0x29);
+ write_data(par, 0x2e);
+ write_data(par, 0x30);
+ write_data(par, 0x30);
+ write_data(par, 0x39);
+ write_data(par, 0x3f);
+ write_data(par, 0x00);
+ write_data(par, 0x07);
+ write_data(par, 0x03);
+ write_data(par, 0x10);
+
+
+ // DISPON - Display On
+ write_cmd(par, 0x29);
+ mdelay(100);
+
+ // NORON - Partial off (Normal)
+ write_cmd(par, 0x13);
+ mdelay(10);
+
+ par->throttle_speed = 0;
+
+ return 0;
+}
+
+
+int sainsmart18fb_write_vmem(struct fbtft_par *par, size_t offset, size_t len)
+{
+ u16 *vmem16 = (u16 *)(par->info->screen_base + offset);
+ u16 *txbuf16 = NULL;
+ size_t remain = len;
+ size_t to_copy;
+ int i;
+ int ret = 0;
+ u16 val;
+ unsigned red, green, blue;
+
+ dev_dbg(par->info->device, "sainsmart18fb_write_vmem: offset=%d, len=%d\n", offset, len);
+
+ if (par->dc != -1)
+ gpio_set_value(par->dc, 1);
+
+ // sanity check
+ if (!par->txbuf.buf) {
+ dev_err(par->info->device, "sainsmart18fb_write_vmem: txbuf.buf is needed to do conversion\n");
+ return -1;
+ }
+
+ while (remain) {
+ to_copy = remain > par->txbuf.len ? par->txbuf.len : remain;
+ txbuf16 = (u16 *)par->txbuf.buf;
+ dev_dbg(par->info->device, " to_copy=%d, remain=%d\n", to_copy, remain - to_copy);
+ for (i=0;i<to_copy;i+=2) {
+ val = *vmem16++;
+// *txbuf16++ = swab16(*vmem16++);
+
+ // Convert to BGR565
+ red = (val >> par->info->var.red.offset) & ((1<<par->info->var.red.length) - 1);
+ green = (val >> par->info->var.green.offset) & ((1<<par->info->var.green.length) - 1);
+ blue = (val >> par->info->var.blue.offset) & ((1<<par->info->var.blue.length) - 1);
+ val = (blue <<11) | (green <<5) | red;
+
+#ifdef __LITTLE_ENDIAN
+ *txbuf16++ = swab16(val);
+#else
+ *txbuf16++ = val;
+#endif
+ }
+ ret = par->fbtftops.write(par, par->txbuf.buf, to_copy);
+ if (ret < 0)
+ return ret;
+ remain -= to_copy;
+ }
+
+ return ret;
+}
+
+
+
+struct fbtft_display adafruit22_display = {
+ .width = WIDTH,
+ .height = HEIGHT,
+ .bpp = BPP,
+ .fps = FPS,
+ .txbuflen = TXBUFLEN,
+};
+
+
+static int __devinit sainsmart18fb_probe(struct spi_device *spi)
+{
+ int chip = spi_get_device_id(spi)->driver_data;
+ struct fb_info *info;
+ struct fbtft_par *par;
+ int ret;
+
+//--------------------------------------------------------------------------------
+
+//[297346.179960] hx8340fb spi0.0: spi->dev.platform_data bf1e48d0
+//dev_err(&spi->dev, "spi->dev.platform_data %p\n", spi->dev.platform_data);
+
+
+if (spi->chip_select == 0) {
+ pr_err("%s: only cs=1 supported\n",
+ DRVNAME);
+ return -EINVAL;
+}
+
+/*
+spi->dev.platform_data = &adafruit22_pdata;
+
+spi->max_speed_hz = 16000000;
+spi->mode = SPI_MODE_0;
+spi->bits_per_word=8;
+ret = spi->master->setup(spi);
+if (ret)
+ pr_err("spi->master->setup(spi) returned %d\n", ret);
+*/
+//--------------------------------------------------------------------------------
+
+
+ dev_dbg(&spi->dev, "probe()\n");
+
+ if (chip != ST7735_DISPLAY_AF_TFT18) {
+ dev_err(&spi->dev, "only the %s device is supported\n",
+ to_spi_driver(spi->dev.driver)->id_table->name);
+ return -EINVAL;
+ }
+
+ if (!(spi->mode == SPI_MODE_0 && spi->bits_per_word == 8)) {
+ dev_err(&spi->dev, "SPI setup not supported (must be 8-bit SPI_MODE_0)\n");
+ return -EINVAL;
+ }
+
+ info = fbtft_framebuffer_alloc(&adafruit22_display, &spi->dev);
+ if (!info)
+ return -ENOMEM;
+
+ par = info->par;
+ par->spi = spi;
+ par->fbtftops.init_display = sainsmart18fb_init_display;
+
+ par->fbtftops.write_vmem = sainsmart18fb_write_vmem;
+
+/*
+ // BGR
+ info->var.red.offset = 0;
+ info->var.green.offset = 5;
+ info->var.blue.offset = 11;
+*/
+
+ ret = fbtft_register_framebuffer(info);
+ if (ret < 0)
+ goto fbreg_fail;
+
+ if (par->dc < 0) {
+ dev_err(&spi->dev, "Missing info about D/C gpio. Aborting.\n");
+ goto fbreg_fail;
+ }
+
+ return 0;
+
+fbreg_fail:
+ fbtft_framebuffer_release(info);
+
+ return ret;
+}
+
+static int __devexit sainsmart18fb_remove(struct spi_device *spi)
+{
+ struct fb_info *info = spi_get_drvdata(spi);
+
+ dev_dbg(&spi->dev, "remove()\n");
+
+ if (info) {
+ fbtft_unregister_framebuffer(info);
+ fbtft_framebuffer_release(info);
+ }
+
+ return 0;
+}
+
+static const struct spi_device_id sainsmart18fb_ids[] = {
+ { DRVNAME, ST7735_DISPLAY_AF_TFT18 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(spi, sainsmart18fb_ids);
+
+static struct spi_driver sainsmart18fb_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ },
+ .id_table = sainsmart18fb_ids,
+ .probe = sainsmart18fb_probe,
+ .remove = __devexit_p(sainsmart18fb_remove),
+};
+
+static int __init sainsmart18fb_init(void)
+{
+ pr_debug("\n\n"DRVNAME" - init\n");
+ return spi_register_driver(&sainsmart18fb_driver);
+}
+
+static void __exit sainsmart18fb_exit(void)
+{
+ pr_debug(DRVNAME" - exit\n");
+ spi_unregister_driver(&sainsmart18fb_driver);
+}
+
+/* ------------------------------------------------------------------------- */
+
+module_init(sainsmart18fb_init);
+module_exit(sainsmart18fb_exit);
+
+MODULE_DESCRIPTION("FB driver for the Sainsmart 1.8\" LCD display");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
View
159 spidevices.c
@@ -0,0 +1,159 @@
+#define DEBUG
+/*
+ *
+ * Copyright (C) 2013, Noralf Tronnes
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+//#include <linux/errno.h>
+//#include <linux/init.h>
+#include <linux/spi/spi.h>
+
+//#include <linux/fbtft.h>
+#include "fbtft.h"
+
+
+#define DRVNAME "spidevices"
+
+struct spi_device *spi_device0;
+struct spi_device *spi_device1;
+
+
+static int spi_device_found(struct device *dev, void *data)
+{
+ struct spi_device *spi = container_of(dev, struct spi_device, dev);
+
+ pr_debug(" %s %s %dkHz %d bits mode=0x%02X\n", spi->modalias, dev_name(dev), spi->max_speed_hz/1000, spi->bits_per_word, spi->mode);
+
+
+ return 0;
+}
+
+
+static void pr_spi_devices(void)
+{
+ pr_debug("SPI devices:\n");
+ bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found);
+}
+
+
+
+static const struct fbtft_gpio adafruit22_gpios[] = {
+ { "reset", 24 },
+ { },
+};
+
+static struct fbtft_platform_data adafruit22_pdata = {
+ .gpios = adafruit22_gpios,
+};
+
+static const struct fbtft_gpio sainsmart18_gpios[] = {
+ { "reset", 24 },
+ { "dc", 23 },
+ { },
+};
+
+static struct fbtft_platform_data sainsmart18_pdata = {
+ .gpios = sainsmart18_gpios,
+};
+
+
+
+static struct spi_board_info chips[] = {
+ {
+ .modalias = "adafruit22fb",
+ .max_speed_hz = 32000000,
+ .bus_num = 0,
+ .chip_select = 0,
+ .mode = SPI_MODE_3,
+ .platform_data = &adafruit22_pdata,
+ }, {
+ .modalias = "sainsmart18fb",
+ .max_speed_hz = 16000000,
+ .bus_num = 0,
+ .chip_select = 1,
+ .mode = SPI_MODE_0,
+ .platform_data = &sainsmart18_pdata,
+ }
+};
+
+#define SPI_BUS 0
+
+static int __init spidevices_init(void)
+{
+ struct spi_master *spi_master;
+ struct device *dev;
+ int ret = 0;
+
+ pr_debug("\n\n"DRVNAME" - init\n");
+ pr_spi_devices();
+
+
+ dev = bus_find_device_by_name(&spi_bus_type, NULL, "spi0.0");
+ if (dev) {
+ pr_err("Deleting spi0.0\n");
+ device_del(dev);
+ }
+
+ dev = bus_find_device_by_name(&spi_bus_type, NULL, "spi0.1");
+ if (dev) {
+ pr_err("Deleting spi0.1\n");
+ device_del(dev);
+ }
+
+
+ spi_master = spi_busnum_to_master(SPI_BUS);
+ if (!spi_master) {
+ pr_err("spi_busnum_to_master(%d) returned NULL\n", SPI_BUS);
+ return -1;
+ }
+
+ spi_device0 = spi_new_device(spi_master, &chips[0]);
+ if (!spi_device0) {
+ pr_err("spi_new_device() returned NULL\n");
+ ret = -1;
+ goto init_out;
+ }
+
+ spi_device1 = spi_new_device(spi_master, &chips[1]);
+ if (!spi_device1) {
+ pr_err("spi_new_device() returned NULL\n");
+ ret = -1;
+ goto init_out;
+ }
+
+
+ pr_spi_devices();
+
+
+init_out:
+ put_device(&spi_master->dev);
+
+ return ret;
+}
+
+static void __exit spidevices_exit(void)
+{
+ pr_debug(DRVNAME" - exit\n");
+
+ device_del(&spi_device0->dev);
+ kfree(spi_device0);
+
+ device_del(&spi_device1->dev);
+ kfree(spi_device1);
+
+}
+
+/* ------------------------------------------------------------------------- */
+
+module_init(spidevices_init);
+module_exit(spidevices_exit);
+
+MODULE_DESCRIPTION("SPI device adder");
+MODULE_AUTHOR("Noralf Tronnes");
+MODULE_LICENSE("GPL");

0 comments on commit 4a441b8

Please sign in to comment.