video/fbtft: Add notro fbtft device driver

Change-Id: Ibcdc3122a24e7a10bdc05def6e5f70cee20568ee
This commit is contained in:
john lee
2015-01-09 14:33:49 +09:00
committed by Dongjin Kim
parent 5992f71999
commit 319d62fcc7
41 changed files with 11481 additions and 1 deletions

View File

@@ -2950,7 +2950,7 @@ CONFIG_FB_SYS_FOPS=m
CONFIG_FB_DEFERRED_IO=y
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
# CONFIG_FB_BACKLIGHT is not set
CONFIG_FB_BACKLIGHT=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_FB_TILEBLITTING=y
@@ -3007,6 +3007,34 @@ CONFIG_LOGO_LINUX_MONO=y
CONFIG_LOGO_LINUX_VGA16=y
CONFIG_LOGO_LINUX_CLUT224=y
# CONFIG_FB_SSD1307 is not set
CONFIG_FB_TFT=m
# CONFIG_FB_TFT_AGM1264K_FL is not set
# CONFIG_FB_TFT_BD663474 is not set
# CONFIG_FB_TFT_HX8340BN is not set
# CONFIG_FB_TFT_HX8347D is not set
# CONFIG_FB_TFT_HX8353D is not set
# CONFIG_FB_TFT_ILI9320 is not set
# CONFIG_FB_TFT_ILI9325 is not set
CONFIG_FB_TFT_ILI9340=m
# CONFIG_FB_TFT_ILI9341 is not set
# CONFIG_FB_TFT_ILI9481 is not set
# CONFIG_FB_TFT_ILI9486 is not set
# CONFIG_FB_TFT_PCD8544 is not set
# CONFIG_FB_TFT_RA8875 is not set
# CONFIG_FB_TFT_S6D02A1 is not set
# CONFIG_FB_TFT_S6D1121 is not set
# CONFIG_FB_TFT_SSD1289 is not set
# CONFIG_FB_TFT_SSD1306 is not set
# CONFIG_FB_TFT_SSD1331 is not set
# CONFIG_FB_TFT_SSD1351 is not set
# CONFIG_FB_TFT_ST7735R is not set
# CONFIG_FB_TFT_TINYLCD is not set
# CONFIG_FB_TFT_TLS8204 is not set
# CONFIG_FB_TFT_UC1701 is not set
# CONFIG_FB_TFT_UPD161704 is not set
# CONFIG_FB_TFT_WATTEROTT is not set
CONFIG_FB_FLEX=m
CONFIG_FB_TFT_FBTFT_DEVICE=m
CONFIG_SOUND=y
# CONFIG_SOUND_OSS_CORE is not set
CONFIG_SND=y

View File

@@ -2513,4 +2513,6 @@ config FB_SSD1307
This driver implements support for the Solomon SSD1307
OLED controller over I2C.
source "drivers/video/fbtft/Kconfig"
endmenu

View File

@@ -177,3 +177,4 @@ obj-$(CONFIG_VIDEOMODE_HELPERS) += display_timing.o videomode.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_VIDEOMODE_HELPERS) += of_display_timing.o of_videomode.o
endif
obj-y += fbtft/

87
drivers/video/fbtft/.gitignore vendored Normal file
View File

@@ -0,0 +1,87 @@
# 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
*.o.*
*.a
*.s
*.ko
*.so
*.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
*.orig
*~
\#*#

169
drivers/video/fbtft/Kconfig Normal file
View File

@@ -0,0 +1,169 @@
menuconfig FB_TFT
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
select FB_BACKLIGHT
config FB_TFT_AGM1264K_FL
tristate "FB driver for the AGM1264K-FL LCD display"
depends on FB_TFT
help
Framebuffer support for the AGM1264K-FL LCD display (two Samsung KS0108 compatable chips)
config FB_TFT_BD663474
tristate "FB driver for the BD663474 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for BD663474
config FB_TFT_HX8340BN
tristate "FB driver for the HX8340BN LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for HX8340BN
config FB_TFT_HX8347D
tristate "FB driver for the HX8347D LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for HX8347D
config FB_TFT_HX8353D
tristate "FB driver for the HX8353D LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for HX8353D
config FB_TFT_ILI9320
tristate "FB driver for the ILI9320 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ILI9320
config FB_TFT_ILI9325
tristate "FB driver for the ILI9325 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ILI9325
config FB_TFT_ILI9340
tristate "FB driver for the ILI9340 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ILI9340
config FB_TFT_ILI9341
tristate "FB driver for the ILI9341 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ILI9341
config FB_TFT_ILI9481
tristate "FB driver for the ILI9481 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ILI9481
config FB_TFT_ILI9486
tristate "FB driver for the ILI9486 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ILI9486
config FB_TFT_PCD8544
tristate "FB driver for the PCD8544 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for PCD8544
config FB_TFT_RA8875
tristate "FB driver for the RA8875 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for RA8875
config FB_TFT_S6D02A1
tristate "FB driver for the S6D02A1 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for S6D02A1
config FB_TFT_S6D1121
tristate "FB driver for the S6D1211 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for S6D1121
config FB_TFT_SSD1289
tristate "FB driver for the SSD1289 LCD Controller"
depends on FB_TFT
help
Framebuffer support for SSD1289
config FB_TFT_SSD1306
tristate "FB driver for the SSD1306 OLED Controller"
depends on FB_TFT
help
Framebuffer support for SSD1306
config FB_TFT_SSD1331
tristate "FB driver for the SSD1331 LCD Controller"
depends on FB_TFT
help
Framebuffer support for SSD1331
config FB_TFT_SSD1351
tristate "FB driver for the SSD1351 LCD Controller"
depends on FB_TFT
help
Framebuffer support for SSD1351
config FB_TFT_ST7735R
tristate "FB driver for the ST7735R LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for ST7735R
config FB_TFT_TINYLCD
tristate "FB driver for tinylcd.com display"
depends on FB_TFT
help
Custom Framebuffer support for tinylcd.com display
config FB_TFT_TLS8204
tristate "FB driver for the TLS8204 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for TLS8204
config FB_TFT_UC1701
tristate "FB driver for the UC1701 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for UC1701
config FB_TFT_UPD161704
tristate "FB driver for the uPD161704 LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for uPD161704
config FB_TFT_WATTEROTT
tristate "FB driver for the WATTEROTT LCD Controller"
depends on FB_TFT
help
Generic Framebuffer support for WATTEROTT
config FB_FLEX
tristate "Generic FB driver for TFT LCD displays"
depends on FB_TFT
help
Generic Framebuffer support for TFT LCD displays.
config FB_TFT_FBTFT_DEVICE
tristate "Module to for adding FBTFT devices"
depends on FB_TFT

View File

@@ -0,0 +1,60 @@
ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
# Optionally, include config file to allow out of tree kernel modules build
-include $(src)/.config
# Core module
obj-$(CONFIG_FB_TFT) += fbtft.o
fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o
# drivers
obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o
obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o
obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o
obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o
obj-$(CONFIG_FB_TFT_HX8353D) += fb_hx8353d.o
obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o
obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o
obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o
obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o
obj-$(CONFIG_FB_TFT_ILI9481) += fb_ili9481.o
obj-$(CONFIG_FB_TFT_ILI9486) += fb_ili9486.o
obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o
obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o
obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o
obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o
obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o
obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o
obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o
obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o
obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o
obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o
obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o
obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o
obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o
obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o
obj-$(CONFIG_FB_FLEX) += flexfb.o
# Device modules
obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o
else
# normal makefile
KDIR ?= /lib/modules/`uname -r`/build
default: .config
$(MAKE) -C $(KDIR) M=$$PWD modules
.config:
grep config Kconfig | cut -d' ' -f2 | sed 's@^@CONFIG_@; s@$$@=m@' > .config
install:
$(MAKE) -C $(KDIR) M=$$PWD modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions \
modules.order Module.symvers
endif

View File

@@ -0,0 +1,32 @@
FBTFT
=========
Linux Framebuffer drivers for small TFT LCD display modules.
The module 'fbtft' makes writing drivers for some of these displays very easy.
Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution.
INSTALLATION
Download kernel sources
From Linux 3.15
cd drivers/video/fbdev/fbtft
git clone https://github.com/notro/fbtft.git
Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig"
Add to drivers/video/fbdev/Makefile: obj-y += fbtft/
Before Linux 3.15
cd drivers/video
git clone https://github.com/notro/fbtft.git
Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig"
Add to drivers/video/Makefile: obj-y += fbtft/
Enable driver(s) in menuconfig and build the kernel
See wiki for more information: https://github.com/notro/fbtft/wiki
Source: https://github.com/notro/fbtft/

View File

@@ -0,0 +1,414 @@
/*
* FBTFT Device Tree part for the Raspberry Pi
* Add this file to the end of the *rpi-b.dts file
*
* Please keep it sorted alphabetically on display name
*
* Notes:
* - use ads7846 instead of tsc2046 to get module autoloading
* - use polarity 1 to drive backlight initially low (off):
* led-gpios = <&gpio 18 1>;
*/
&spi0 {
/* this is provided here to make it easy to enable SPI when editing this file */
// status = "okay";
};
/*
* Texy
* 2.8" TFT + Touch Shield Board (320x240) HY28A
*
*/
&spi0 {
hy28a@0{
compatible = "ilitek,ili9320";
reg = <0>;
status = "disabled";
spi-max-frequency = <32000000>;
spi-cpol;
spi-cpha;
rotate = <270>;
bgr;
fps = <50>;
buswidth = <8>;
startbyte = <0x70>;
reset-gpios = <&gpio 25 0>;
led-gpios = <&gpio 18 1>;
debug = <0>;
};
hy28a_ts@1 {
compatible = "ti,ads7846";
reg = <1>;
status = "disabled";
spi-max-frequency = <2000000>;
interrupts = <3 17>;
pendown-gpio = <&gpio 17 0>;
ti,x-plate-ohms = /bits/ 16 <100>;
ti,pressure-max = /bits/ 16 <255>;
};
};
/*
* Texy
* 2.8" TFT + Touch Shield Board (320x240) HY28B
* NOT TESTED
*/
&spi0 {
hy28b@0{
compatible = "ilitek,ili9325";
reg = <0>;
status = "disabled";
spi-max-frequency = <48000000>;
rotate = <270>;
bgr;
fps = <50>;
buswidth = <8>;
startbyte = <0x70>;
reset-gpios = <&gpio 25 0>;
led-gpios = <&gpio 18 1>;
gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7";
init = <0x10000e7 0x0010
0x1000000 0x0001
0x1000001 0x0100
0x1000002 0x0700
0x1000003 0x1030
0x1000004 0x0000
0x1000008 0x0207
0x1000009 0x0000
0x100000a 0x0000
0x100000c 0x0001
0x100000d 0x0000
0x100000f 0x0000
0x1000010 0x0000
0x1000011 0x0007
0x1000012 0x0000
0x1000013 0x0000
0x2000032
0x1000010 0x1590
0x1000011 0x0227
0x2000032
0x1000012 0x009c
0x2000032
0x1000013 0x1900
0x1000029 0x0023
0x100002b 0x000e
0x2000032
0x1000020 0x0000
0x1000021 0x0000
0x2000032
0x1000050 0x0000
0x1000051 0x00ef
0x1000052 0x0000
0x1000053 0x013f
0x1000060 0xa700
0x1000061 0x0001
0x100006a 0x0000
0x1000080 0x0000
0x1000081 0x0000
0x1000082 0x0000
0x1000083 0x0000
0x1000084 0x0000
0x1000085 0x0000
0x1000090 0x0010
0x1000092 0x0000
0x1000093 0x0003
0x1000095 0x0110
0x1000097 0x0000
0x1000098 0x0000
0x1000007 0x0133
0x1000020 0x0000
0x1000021 0x0000
0x2000064>;
debug = <0>;
};
hy28b_ts@1 {
compatible = "ti,ads7846";
reg = <1>;
status = "disabled";
spi-max-frequency = <2000000>;
interrupts = <3 17>;
pendown-gpio = <&gpio 17 0>;
ti,x-plate-ohms = /bits/ 16 <100>;
ti,pressure-max = /bits/ 16 <255>;
};
};
/*
* ITEAD
* ITDB02-2.8
*
*/
/ {
itdb28 {
compatible = "ilitek,ili9325";
status = "disabled";
rotate = <0>;
bgr;
buswidth = <8>;
reset-gpios = <&gpio 17 0>;
dc-gpios = <&gpio 3 0>;
cs-gpios = <&gpio 27 0>;
wr-gpios = <&gpio 2 0>;
db-gpios = <&gpio 9 0>,
<&gpio 11 0>,
<&gpio 18 0>,
<&gpio 23 0>,
<&gpio 24 0>,
<&gpio 25 0>,
<&gpio 8 0>,
<&gpio 7 0>;
/* LED pin drives backlight directly. Use transistor (50mA) */
/* led-gpios = <&gpio 4 1>; */
debug = <0>;
};
};
/*
* Watterott
* RPi-Display - 2.8" Touch-Display (320x240)
*
*/
&spi0 {
rpi-display@0{
compatible = "ilitek,ili9341";
reg = <0>;
status = "disabled";
spi-max-frequency = <32000000>;
rotate = <270>;
bgr;
fps = <30>;
buswidth = <8>;
reset-gpios = <&gpio 23 0>;
dc-gpios = <&gpio 24 0>;
led-gpios = <&gpio 18 1>;
debug = <0>;
};
rpi-display_ts@1 {
compatible = "ti,ads7846";
reg = <1>;
status = "disabled";
spi-max-frequency = <2000000>;
interrupts = <3 25>;
pendown-gpio = <&gpio 25 0>;
ti,x-plate-ohms = /bits/ 16 <60>;
ti,pressure-max = /bits/ 16 <255>;
};
};
/*
* Ozzmaker
* PiScreen - 3.5" TFT touchscreen (480x320)
*
*/
&spi0 {
piscreen@0{
compatible = "ilitek,ili9486";
reg = <0>;
status = "disabled";
spi-max-frequency = <20000000>;
rotate = <270>;
bgr;
fps = <30>;
buswidth = <8>;
reset-gpios = <&gpio 25 0>;
dc-gpios = <&gpio 24 0>;
led-gpios = <&gpio 22 1>;
init = <0x10000b0 0x00
0x1000011
0x20000FF
0x100003A 0x55
0x1000036 0x28
0x10000C2 0x44
0x10000C5 0x00 0x00 0x00 0x00
0x10000E0 0x0F 0x1F 0x1C 0x0C 0x0F 0x08 0x48 0x98 0x37 0x0A 0x13 0x04 0x11 0x0D 0x00
0x10000E1 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00
0x10000E2 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00
0x1000011
0X1000029>;
debug = <0>;
};
piscreen_ts@1 {
compatible = "ti,ads7846";
reg = <1>;
status = "disabled";
spi-max-frequency = <2000000>;
interrupts = <3 17>;
pendown-gpio = <&gpio 17 0>;
ti,x-plate-ohms = /bits/ 16 <100>;
ti,pressure-max = /bits/ 16 <255>;
};
};
/*
* Adafruit
* PiTFT Mini Kit - 320x240 2.8" TFT+Touchscreen for Raspberry Pi
*
* Couldn't get touch controller to work
*/
&gpio {
stmpets_pins: stmpets_pins {
brcm,pins = <24>;
brcm,function = <0>; /* gpio in */
brcm,pull = <2>; /* pull up */
};
};
&spi0 {
pitft@0{
compatible = "ilitek,ili9340";
reg = <0>;
status = "disabled";
spi-max-frequency = <32000000>;
rotate = <90>;
bgr;
buswidth = <8>;
dc-gpios = <&gpio 25 0>;
init = <0x1000001
0x2000005
0x1000028
0x10000EF 0x03 0x80 0x02
0x10000CF 0x00 0xC1 0x30
0x10000ED 0x64 0x03 0x12 0x81
0x10000E8 0x85 0x00 0x78
0x10000CB 0x39 0x2C 0x00 0x34 0x02
0x10000F7 0x20
0x10000EA 0x00 0x00
0x10000C0 0x23
0x10000C1 0x10
0x10000C5 0x3e 0x28
0x10000C7 0x86
0x100003A 0x55
0x10000B1 0x00 0x18
0x10000B6 0x08 0x82 0x27
0x10000F2 0x00
0x1000026 0x01
0x10000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00
0x10000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F
0x1000011
0x2000064
0x1000029
0x2000014>;
debug = <0>;
};
/*
Touchscreen didn't work. I guess it has something to do with it being an interrupt controller.
I have never tried this before with DT and ARCH_BCM2708.
On ARCH_BCM2835 it should be OK, since the GPIO controller acts as an interrupt controller as well.
(stmpe_device can't be used from 3.16, because irq_base can't be set anymore)
[ 8.889056] irq: irq_create_mapping(0xc3008800, 0xc2)
[ 8.895698] irq: -> using domain @c3008800
[ 8.900796] irq: -> existing mapping on virq 194
[ 8.925048] stmpe-spi spi0.1: stmpe610 detected, chip id: 0x811
[ 8.936650] irq: Added domain (null)
[ 8.941780] irq: irq_create_mapping(0xc1dc1a20, 0x0)
[ 8.949047] irq: -> using domain @c1dc1a20
[ 8.954421] irq: -> virq allocation failed
[ 8.959926] irq: irq_create_mapping(0xc1dc1a20, 0x1)
[ 8.967366] irq: -> using domain @c1dc1a20
[ 8.972870] irq: -> virq allocation failed
*/
pitft_ts@1 {
compatible = "st,stmpe610";
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
status = "disabled";
pinctrl-names = "default";
pinctrl-0 = <&stmpets_pins>;
spi-max-frequency = <500000>;
interrupts = <3 24>;
interrupt-parent = <&intc>;
interrupt-controller;
stmpe_touchscreen {
compatible = "st,stmpe-ts";
st,sample-time = <4>;
st,mod-12b = <1>;
st,ref-sel = <0>;
st,adc-freq = <2>;
st,ave-ctrl = <3>;
st,touch-det-delay = <4>;
st,settling = <2>;
st,fraction-z = <7>;
st,i-drive = <0>;
};
};
};
/*
* NeoSec LLC
* 3.5 inch TFT Display (320x480)
*
*/
&spi0 {
tinylcd35@0{
compatible = "neosec,tinylcd";
reg = <0>;
status = "disabled";
spi-max-frequency = <48000000>;
rotate = <270>;
bgr;
fps = <50>;
buswidth = <8>;
reset-gpios = <&gpio 25 0>;
dc-gpios = <&gpio 24 0>;
led-gpios = <&gpio 18 1>;
debug = <0>;
};
tinylcd35@1 {
compatible = "ti,ads7846";
reg = <1>;
status = "disabled";
spi-max-frequency = <2000000>;
interrupts = <3 3>;
pendown-gpio = <&gpio 3 0>;
ti,x-plate-ohms = /bits/ 16 <100>;
ti,pressure-max = /bits/ 16 <255>;
};
};

View File

@@ -0,0 +1,462 @@
/*
* FB driver for Two KS0108 LCD controllers in AGM1264K-FL display
*
* Copyright (C) 2014 ololoshka2871
*
* 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include "fbtft.h"
/* Uncomment text line to use negative image on display */
/*#define NEGATIVE*/
#define WHITE 0xff
#define BLACK 0
#define DRVNAME "fb_agm1264k-fl"
#define WIDTH 64
#define HEIGHT 64
#define TOTALWIDTH (WIDTH * 2) /* because 2 x ks0108 in one display */
#define FPS 20
#define EPIN gpio.wr
#define RS gpio.dc
#define RW gpio.aux[2]
#define CS0 gpio.aux[0]
#define CS1 gpio.aux[1]
/* diffusing error (<28>Floyd-Steinberg<72>) */
#define DIFFUSING_MATRIX_WIDTH 2
#define DIFFUSING_MATRIX_HEIGHT 2
static const signed char
diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = {
{-1, 3},
{3, 2},
};
static const unsigned char gamma_correction_table[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6,
6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13,
13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21,
22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32,
33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45,
46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81,
82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102,
103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121,
123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143,
145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166,
168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192,
194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219,
221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248,
251, 253, 255
};
static int init_display(struct fbtft_par *par)
{
u8 i;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
for (i = 0; i < 2; ++i) {
write_reg(par, i, 0x3f); /* display on */
write_reg(par, i, 0x40); /* set x to 0 */
write_reg(par, i, 0xb0); /* set page to 0 */
write_reg(par, i, 0xc0); /* set start line to 0 */
}
return 0;
}
void reset(struct fbtft_par *par)
{
if (par->gpio.reset == -1)
return;
fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__);
gpio_set_value(par->gpio.reset, 0);
udelay(20);
gpio_set_value(par->gpio.reset, 1);
mdelay(120);
}
/* Check if all necessary GPIOS defined */
static int verify_gpios(struct fbtft_par *par)
{
int i;
fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device,
"%s()\n", __func__);
if (par->EPIN < 0) {
dev_err(par->info->device,
"Missing info about 'wr' (aka E) gpio. Aborting.\n");
return -EINVAL;
}
for (i = 0; i < 8; ++i) {
if (par->gpio.db[i] < 0) {
dev_err(par->info->device,
"Missing info about 'db[%i]' gpio. Aborting.\n",
i);
return -EINVAL;
}
}
if (par->CS0 < 0) {
dev_err(par->info->device,
"Missing info about 'cs0' gpio. Aborting.\n");
return -EINVAL;
}
if (par->CS1 < 0) {
dev_err(par->info->device,
"Missing info about 'cs1' gpio. Aborting.\n");
return -EINVAL;
}
if (par->RW < 0) {
dev_err(par->info->device,
"Missing info about 'rw' gpio. Aborting.\n");
return -EINVAL;
}
return 0;
}
static unsigned long
request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio)
{
fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device,
"%s('%s')\n", __func__, gpio->name);
if (strcasecmp(gpio->name, "wr") == 0) {
/* left ks0108 E pin */
par->EPIN = gpio->gpio;
return GPIOF_OUT_INIT_LOW;
} else if (strcasecmp(gpio->name, "cs0") == 0) {
/* left ks0108 controller pin */
par->CS0 = gpio->gpio;
return GPIOF_OUT_INIT_HIGH;
} else if (strcasecmp(gpio->name, "cs1") == 0) {
/* right ks0108 controller pin */
par->CS1 = gpio->gpio;
return GPIOF_OUT_INIT_HIGH;
}
/* if write (rw = 0) e(1->0) perform write */
/* if read (rw = 1) e(0->1) set data on D0-7*/
else if (strcasecmp(gpio->name, "rw") == 0) {
par->RW = gpio->gpio;
return GPIOF_OUT_INIT_LOW;
}
return FBTFT_GPIO_NO_MATCH;
}
/* This function oses to enter commands
* first byte - destination controller 0 or 1
* folowing - commands
*/
static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
va_list args;
int i, ret;
u8 *buf = (u8 *)par->buf;
if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
va_start(args, len);
for (i = 0; i < len; i++)
buf[i] = (u8)va_arg(args, unsigned int);
va_end(args);
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
par->info->device, u8, buf, len, "%s: ", __func__);
}
va_start(args, len);
*buf = (u8)va_arg(args, unsigned int);
if (*buf > 1) {
va_end(args);
dev_err(par->info->device, "%s: Incorrect chip sellect request (%d)\n",
__func__, *buf);
return;
}
/* select chip */
if (*buf) {
/* cs1 */
gpio_set_value(par->CS0, 1);
gpio_set_value(par->CS1, 0);
} else {
/* cs0 */
gpio_set_value(par->CS0, 0);
gpio_set_value(par->CS1, 1);
}
gpio_set_value(par->RS, 0); /* RS->0 (command mode) */
len--;
if (len) {
i = len;
while (i--)
*buf++ = (u8)va_arg(args, unsigned int);
ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8)));
if (ret < 0) {
va_end(args);
dev_err(par->info->device, "%s: write() failed and returned %d\n",
__func__, ret);
return;
}
}
va_end(args);
}
static struct
{
int xs, ys_page, xe, ye_page;
} addr_win;
/* save display writing zone */
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
addr_win.xs = xs;
addr_win.ys_page = ys / 8;
addr_win.xe = xe;
addr_win.ye_page = ye / 8;
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__,
addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page);
}
static void
construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src,
int xs, int xe, int y)
{
int x, i;
for (x = xs; x < xe; ++x) {
u8 res = 0;
for (i = 0; i < 8; i++)
if (src[(y * 8 + i) * par->info->var.xres + x])
res |= 1 << i;
#ifdef NEGATIVE
*dest++ = res;
#else
*dest++ = ~res;
#endif
}
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16 = (u16 *)par->info->screen_base;
u8 *buf = par->txbuf.buf;
int x, y;
int ret = 0;
/* buffer to convert RGB565 -> grayscale16 -> Ditherd image 1bpp */
signed short *convert_buf = kmalloc(par->info->var.xres *
par->info->var.yres * sizeof(signed short), GFP_NOIO);
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
/* converting to grayscale16 */
for (x = 0; x < par->info->var.xres; ++x)
for (y = 0; y < par->info->var.yres; ++y) {
u16 pixel = vmem16[y * par->info->var.xres + x];
u16 b = pixel & 0x1f;
u16 g = (pixel & (0x3f << 5)) >> 5;
u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6);
pixel = (299 * r + 587 * g + 114 * b) / 200;
if (pixel > 255)
pixel = 255;
/* gamma-correction by table */
convert_buf[y * par->info->var.xres + x] =
(signed short)gamma_correction_table[pixel];
}
/* Image Dithering */
for (x = 0; x < par->info->var.xres; ++x)
for (y = 0; y < par->info->var.yres; ++y) {
signed short pixel =
convert_buf[y * par->info->var.xres + x];
signed short error_b = pixel - BLACK;
signed short error_w = pixel - WHITE;
signed short error;
u16 i, j;
/* what color close? */
if (abs(error_b) >= abs(error_w)) {
/* white */
error = error_w;
pixel = 0xff;
} else {
/* black */
error = error_b;
pixel = 0;
}
error /= 8;
/* diffusion matrix row */
for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i)
/* diffusion matrix column */
for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) {
signed short *write_pos;
signed char coeff;
/* skip pixels out of zone */
if (x + i < 0 ||
x + i >= par->info->var.xres
|| y + j >= par->info->var.yres)
continue;
write_pos = &convert_buf[
(y + j) * par->info->var.xres +
x + i];
coeff = diffusing_matrix[i][j];
if (coeff == -1)
/* pixel itself */
*write_pos = pixel;
else {
signed short p = *write_pos +
error * coeff;
if (p > WHITE)
p = WHITE;
if (p < BLACK)
p = BLACK;
*write_pos = p;
}
}
}
/* 1 string = 2 pages */
for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) {
/* left half of display */
if (addr_win.xs < par->info->var.xres / 2) {
construct_line_bitmap(par, buf, convert_buf,
addr_win.xs, par->info->var.xres / 2, y);
len = par->info->var.xres / 2 - addr_win.xs;
/* select left side (sc0)
* set addr
*/
write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs);
write_reg(par, 0x00, (0x17 << 3) | (u8)y);
/* write bitmap */
gpio_set_value(par->RS, 1); /* RS->1 (data mode) */
ret = par->fbtftops.write(par, buf, len);
if (ret < 0)
dev_err(par->info->device,
"%s: write failed and returned: %d\n",
__func__, ret);
}
/* right half of display */
if (addr_win.xe >= par->info->var.xres / 2) {
construct_line_bitmap(par, buf,
convert_buf, par->info->var.xres / 2,
addr_win.xe + 1, y);
len = addr_win.xe + 1 - par->info->var.xres / 2;
/* select right side (sc1)
* set addr
*/
write_reg(par, 0x01, (1 << 6));
write_reg(par, 0x01, (0x17 << 3) | (u8)y);
/* write bitmap */
gpio_set_value(par->RS, 1); /* RS->1 (data mode) */
par->fbtftops.write(par, buf, len);
if (ret < 0)
dev_err(par->info->device,
"%s: write failed and returned: %d\n",
__func__, ret);
}
}
kfree(convert_buf);
gpio_set_value(par->CS0, 1);
gpio_set_value(par->CS1, 1);
return ret;
}
static int write(struct fbtft_par *par, void *buf, size_t len)
{
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
gpio_set_value(par->RW, 0); /* set write mode */
while (len--) {
u8 i, data;
data = *(u8 *) buf++;
/* set data bus */
for (i = 0; i < 8; ++i)
gpio_set_value(par->gpio.db[i], data & (1 << i));
/* set E */
gpio_set_value(par->EPIN, 1);
udelay(5);
/* unset E - write */
gpio_set_value(par->EPIN, 0);
udelay(1);
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = TOTALWIDTH,
.height = HEIGHT,
.fps = FPS,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.verify_gpios = verify_gpios,
.request_gpios_match = request_gpios_match,
.reset = reset,
.write = write,
.write_register = write_reg8_bus8,
.write_vmem = write_vmem,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display");
MODULE_AUTHOR("ololoshka2871");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,193 @@
/*
* FB driver for the uPD161704 LCD Controller
*
* Copyright (C) 2014 Seong-Woo Kim
*
* Based on fb_ili9325.c by Noralf Tronnes
* Based on ili9325.c by Jeroen Domburg
* Init code from UTFT library by Henning Karlsen
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_bd663474"
#define WIDTH 240
#define HEIGHT 320
#define BPP 16
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
if (par->gpio.cs != -1)
gpio_set_value(par->gpio.cs, 0); /* Activate chip */
par->fbtftops.reset(par);
/* Initialization sequence from Lib_UTFT */
/* oscillator start */
write_reg(par, 0x000,0x0001); /*oscillator 0: stop, 1: operation */
mdelay(10);
/* Power settings */
write_reg(par, 0x100, 0x0000 ); /* power supply setup */
write_reg(par, 0x101, 0x0000 );
write_reg(par, 0x102, 0x3110 );
write_reg(par, 0x103, 0xe200 );
write_reg(par, 0x110, 0x009d );
write_reg(par, 0x111, 0x0022 );
write_reg(par, 0x100, 0x0120 );
mdelay( 20 );
write_reg(par, 0x100, 0x3120 );
mdelay( 80 );
/* Display control */
write_reg(par, 0x001, 0x0100 );
write_reg(par, 0x002, 0x0000 );
write_reg(par, 0x003, 0x1230 );
write_reg(par, 0x006, 0x0000 );
write_reg(par, 0x007, 0x0101 );
write_reg(par, 0x008, 0x0808 );
write_reg(par, 0x009, 0x0000 );
write_reg(par, 0x00b, 0x0000 );
write_reg(par, 0x00c, 0x0000 );
write_reg(par, 0x00d, 0x0018 );
/* LTPS control settings */
write_reg(par, 0x012, 0x0000 );
write_reg(par, 0x013, 0x0000 );
write_reg(par, 0x018, 0x0000 );
write_reg(par, 0x019, 0x0000 );
write_reg(par, 0x203, 0x0000 );
write_reg(par, 0x204, 0x0000 );
write_reg(par, 0x210, 0x0000 );
write_reg(par, 0x211, 0x00ef );
write_reg(par, 0x212, 0x0000 );
write_reg(par, 0x213, 0x013f );
write_reg(par, 0x214, 0x0000 );
write_reg(par, 0x215, 0x0000 );
write_reg(par, 0x216, 0x0000 );
write_reg(par, 0x217, 0x0000 );
/* Gray scale settings */
write_reg(par, 0x300, 0x5343);
write_reg(par, 0x301, 0x1021);
write_reg(par, 0x302, 0x0003);
write_reg(par, 0x303, 0x0011);
write_reg(par, 0x304, 0x050a);
write_reg(par, 0x305, 0x4342);
write_reg(par, 0x306, 0x1100);
write_reg(par, 0x307, 0x0003);
write_reg(par, 0x308, 0x1201);
write_reg(par, 0x309, 0x050a);
/* RAM access settings */
write_reg(par, 0x400, 0x4027 );
write_reg(par, 0x401, 0x0000 );
write_reg(par, 0x402, 0x0000 ); /* First screen drive position (1) */
write_reg(par, 0x403, 0x013f ); /* First screen drive position (2) */
write_reg(par, 0x404, 0x0000 );
write_reg(par, 0x200, 0x0000 );
write_reg(par, 0x201, 0x0000 );
write_reg(par, 0x100, 0x7120 );
write_reg(par, 0x007, 0x0103 );
mdelay( 10 );
write_reg(par, 0x007, 0x0113 );
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R200h = Horizontal GRAM Start Address */
/* R201h = Vertical GRAM Start Address */
case 0:
write_reg(par, 0x0200, xs);
write_reg(par, 0x0201, ys);
break;
case 180:
write_reg(par, 0x0200, WIDTH - 1 - xs);
write_reg(par, 0x0201, HEIGHT - 1 - ys);
break;
case 270:
write_reg(par, 0x0200, WIDTH - 1 - ys);
write_reg(par, 0x0201, xs);
break;
case 90:
write_reg(par, 0x0200, ys);
write_reg(par, 0x0201, HEIGHT - 1 - xs);
break;
}
write_reg(par, 0x202); /* Write Data to GRAM */
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
/* AM: GRAM update direction */
case 0:
write_reg(par, 0x003, 0x1230);
break;
case 180:
write_reg(par, 0x003, 0x1200);
break;
case 270:
write_reg(par, 0x003, 0x1228);
break;
case 90:
write_reg(par, 0x003, 0x1218);
break;
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 16,
.width = WIDTH,
.height = HEIGHT,
.bpp = BPP,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "hitachi,bd663474", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:bd663474");
MODULE_ALIAS("platform:bd663474");
MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller");
MODULE_AUTHOR("Seong-Woo Kim");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,229 @@
/*
* FB driver for the HX8340BN LCD Controller
*
* This display uses 9-bit SPI: Data/Command bit + 8 data bits
* For platforms that doesn't support 9-bit, the driver is capable
* of emulating this using 8-bit transfer.
* This is done by transfering eight 9-bit words in 9 bytes.
*
* 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/init.h>
#include <linux/vmalloc.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_hx8340bn"
#define WIDTH 176
#define HEIGHT 220
#define TXBUFLEN (4 * PAGE_SIZE)
#define DEFAULT_GAMMA "1 3 0E 5 0 2 09 0 6 1 7 1 0 2 2\n" \
"3 3 17 8 4 7 05 7 6 0 3 1 6 0 0 "
static bool emulate;
module_param(emulate, bool, 0);
MODULE_PARM_DESC(emulate, "Force emulation in 9-bit mode");
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
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: ffh,83h,40h */
write_reg(par, 0xC1, 0xFF, 0x83, 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_reg(par, 0x11);
mdelay(150);
/* Undoc'd register? */
write_reg(par, 0xCA, 0x70, 0x00, 0xD9);
/* SETOSC: Set Internal Oscillator (B0h)
This command is used to set internal oscillator related settings */
/* OSC_EN: Enable internal oscillator */
/* Internal oscillator frequency: 125% x 2.52MHz */
write_reg(par, 0xB0, 0x01, 0x11);
/* Drive ability setting */
write_reg(par, 0xC9, 0x90, 0x49, 0x10, 0x28, 0x28, 0x10, 0x00, 0x06);
mdelay(20);
/* SETPWCTR5: Set Power Control 5(B5h)
This command is used to set VCOM Low and VCOM High Voltage */
/* VCOMH 0110101 : 3.925 */
/* VCOML 0100000 : -1.700 */
/* 45h=69 VCOMH: "VMH" + 5d VCOML: "VMH" + 5d */
write_reg(par, 0xB5, 0x35, 0x20, 0x45);
/* 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_reg(par, 0xB4, 0x33, 0x25, 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. */
/* RGB interface: 16 Bit/Pixel */
write_reg(par, 0x3A, 0x05);
/* Display on (29h)
This command is used to recover from DISPLAY OFF mode.
Output from the Frame Memory is enabled. */
write_reg(par, 0x29);
mdelay(10);
return 0;
}
void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
write_reg(par, FBTFT_CASET, 0x00, xs, 0x00, xe);
write_reg(par, FBTFT_RASET, 0x00, ys, 0x00, ye);
write_reg(par, FBTFT_RAMWR);
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* MADCTL - Memory data access control */
/* RGB/BGR can be set with H/W pin SRGB and MADCTL BGR bit */
#define MY (1 << 7)
#define MX (1 << 6)
#define MV (1 << 5)
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x36, (par->bgr << 3));
break;
case 270:
write_reg(par, 0x36, MX | MV | (par->bgr << 3));
break;
case 180:
write_reg(par, 0x36, MX | MY | (par->bgr << 3));
break;
case 90:
write_reg(par, 0x36, MY | MV | (par->bgr << 3));
break;
}
return 0;
}
/*
Gamma Curve selection, GC (only GC0 can be customized):
0 = 2.2, 1 = 1.8, 2 = 2.5, 3 = 1.0
Gamma string format:
OP0 OP1 CP0 CP1 CP2 CP3 CP4 MP0 MP1 MP2 MP3 MP4 MP5 CGM0 CGM1
ON0 ON1 CN0 CN1 CN2 CN3 CN4 MN0 MN1 MN2 MN3 MN4 MN5 XXXX GC
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long mask[] = {
0b1111, 0b1111, 0b11111, 0b1111, 0b1111, 0b1111, 0b11111,
0b111, 0b111, 0b111, 0b111, 0b111, 0b111, 0b11, 0b11,
0b1111, 0b1111, 0b11111, 0b1111, 0b1111, 0b1111, 0b11111,
0b111, 0b111, 0b111, 0b111, 0b111, 0b111, 0b0, 0b0 };
int i, j;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < par->gamma.num_curves; i++)
for (j = 0; j < par->gamma.num_values; j++)
CURVE(i, j) &= mask[i * par->gamma.num_values + j];
write_reg(par, 0x26, 1 << CURVE(1, 14)); /* Gamma Set (26h) */
if (CURVE(1, 14))
return 0; /* only GC0 can be customized */
write_reg(par, 0xC2,
(CURVE(0, 8) << 4) | CURVE(0, 7),
(CURVE(0, 10) << 4) | CURVE(0, 9),
(CURVE(0, 12) << 4) | CURVE(0, 11),
CURVE(0, 2),
(CURVE(0, 4) << 4) | CURVE(0, 3),
CURVE(0, 5),
CURVE(0, 6),
(CURVE(0, 1) << 4) | CURVE(0, 0),
(CURVE(0, 14) << 2) | CURVE(0, 13));
write_reg(par, 0xC3,
(CURVE(1, 8) << 4) | CURVE(1, 7),
(CURVE(1, 10) << 4) | CURVE(1, 9),
(CURVE(1, 12) << 4) | CURVE(1, 11),
CURVE(1, 2),
(CURVE(1, 4) << 4) | CURVE(1, 3),
CURVE(1, 5),
CURVE(1, 6),
(CURVE(1, 1) << 4) | CURVE(1, 0));
mdelay(10);
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.txbuflen = TXBUFLEN,
.gamma_num = 2,
.gamma_len = 15,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8340bn", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:hx8340bn");
MODULE_ALIAS("platform:hx8340bn");
MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,181 @@
/*
* FB driver for the HX8347D LCD Controller
*
* Copyright (C) 2013 Christian Vogelgsang
*
* Based on driver code found here: https://github.com/watterott/r61505u-Adapter
*
* 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/init.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_hx8347d"
#define WIDTH 320
#define HEIGHT 240
#define DEFAULT_GAMMA "0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" \
"0 0 0 0 0 0 0 0 0 0 0 0 0 0"
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
/* driving ability */
write_reg(par, 0xEA, 0x00);
write_reg(par, 0xEB, 0x20);
write_reg(par, 0xEC, 0x0C);
write_reg(par, 0xED, 0xC4);
write_reg(par, 0xE8, 0x40);
write_reg(par, 0xE9, 0x38);
write_reg(par, 0xF1, 0x01);
write_reg(par, 0xF2, 0x10);
write_reg(par, 0x27, 0xA3);
/* power voltage */
write_reg(par, 0x1B, 0x1B);
write_reg(par, 0x1A, 0x01);
write_reg(par, 0x24, 0x2F);
write_reg(par, 0x25, 0x57);
/* VCOM offset */
write_reg(par, 0x23, 0x8D); /* for flicker adjust */
/* power on */
write_reg(par, 0x18, 0x36);
write_reg(par, 0x19, 0x01); /* start osc */
write_reg(par, 0x01, 0x00); /* wakeup */
write_reg(par, 0x1F, 0x88);
mdelay(5);
write_reg(par, 0x1F, 0x80);
mdelay(5);
write_reg(par, 0x1F, 0x90);
mdelay(5);
write_reg(par, 0x1F, 0xD0);
mdelay(5);
/* color selection */
write_reg(par, 0x17, 0x05); /* 65k */
/*panel characteristic */
write_reg(par, 0x36, 0x00);
/*display on */
write_reg(par, 0x28, 0x38);
mdelay(40);
write_reg(par, 0x28, 0x3C);
/* orientation */
write_reg(par, 0x16, 0x60 | (par->bgr << 3));
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
write_reg(par, 0x02, (xs >> 8) & 0xFF);
write_reg(par, 0x03, xs & 0xFF);
write_reg(par, 0x04, (xe >> 8) & 0xFF);
write_reg(par, 0x05, xe & 0xFF);
write_reg(par, 0x06, (ys >> 8) & 0xFF);
write_reg(par, 0x07, ys & 0xFF);
write_reg(par, 0x08, (ye >> 8) & 0xFF);
write_reg(par, 0x09, ye & 0xFF);
write_reg(par, 0x22);
}
/*
Gamma string format:
VRP0 VRP1 VRP2 VRP3 VRP4 VRP5 PRP0 PRP1 PKP0 PKP1 PKP2 PKP3 PKP4 CGM
VRN0 VRN1 VRN2 VRN3 VRN4 VRN5 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 CGM
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long mask[] = {
0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111,
0b1111111, 0b1111111,
0b11111, 0b11111, 0b11111, 0b11111, 0b11111,
0b1111};
int i, j;
int acc = 0;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < par->gamma.num_curves; i++)
for (j = 0; j < par->gamma.num_values; j++) {
acc += CURVE(i, j);
CURVE(i, j) &= mask[j];
}
if (acc == 0) /* skip if all values are zero */
return 0;
for (i = 0; i < par->gamma.num_curves; i++) {
write_reg(par, 0x40 + (i * 0x10), CURVE(i, 0));
write_reg(par, 0x41 + (i * 0x10), CURVE(i, 1));
write_reg(par, 0x42 + (i * 0x10), CURVE(i, 2));
write_reg(par, 0x43 + (i * 0x10), CURVE(i, 3));
write_reg(par, 0x44 + (i * 0x10), CURVE(i, 4));
write_reg(par, 0x45 + (i * 0x10), CURVE(i, 5));
write_reg(par, 0x46 + (i * 0x10), CURVE(i, 6));
write_reg(par, 0x47 + (i * 0x10), CURVE(i, 7));
write_reg(par, 0x48 + (i * 0x10), CURVE(i, 8));
write_reg(par, 0x49 + (i * 0x10), CURVE(i, 9));
write_reg(par, 0x4A + (i * 0x10), CURVE(i, 10));
write_reg(par, 0x4B + (i * 0x10), CURVE(i, 11));
write_reg(par, 0x4C + (i * 0x10), CURVE(i, 12));
}
write_reg(par, 0x5D, (CURVE(1, 0) << 4) | CURVE(0, 0));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.gamma_num = 2,
.gamma_len = 14,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8347d", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:hx8347d");
MODULE_ALIAS("platform:hx8347d");
MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller");
MODULE_AUTHOR("Christian Vogelgsang");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,166 @@
/*
* FB driver for the HX8353D LCD Controller
*
* Copyright (c) 2014 Petr Olivka
* 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/init.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_hx8353d"
#define DEFAULT_GAMMA "50 77 40 08 BF 00 03 0F 00 01 73 00 72 03 B0 0F 08 00 0F"
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
mdelay(150);
/* SETEXTC */
write_reg(par, 0xB9, 0xFF, 0x83, 0x53);
/* RADJ */
write_reg(par, 0xB0, 0x3C, 0x01);
/* VCOM */
write_reg(par, 0xB6, 0x94, 0x6C, 0x50);
/* PWR */
write_reg(par, 0xB1, 0x00, 0x01, 0x1B, 0x03, 0x01, 0x08, 0x77, 0x89);
/* COLMOD */
write_reg(par, 0x3A, 0x05);
/* MEM ACCESS */
write_reg(par, 0x36, 0xC0);
/* SLPOUT - Sleep out & booster on */
write_reg(par, 0x11);
mdelay(150);
/* DISPON - Display On */
write_reg(par, 0x29);
/* RGBSET */
write_reg(par, 0x2D,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30,
32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62);
return 0;
};
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* column address */
write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
/* row adress */
write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
/* memory write */
write_reg(par, 0x2c);
}
#define my (1 << 7)
#define mx (1 << 6)
#define mv (1 << 5)
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* madctl - memory data access control
rgb/bgr:
1. mode selection pin srgb
rgb h/w pin for color filter setting: 0=rgb, 1=bgr
2. madctl rgb bit
rgb-bgr order color filter panel: 0=rgb, 1=bgr */
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x36, mx | my | (par->bgr << 3));
break;
case 270:
write_reg(par, 0x36, my | mv | (par->bgr << 3));
break;
case 180:
write_reg(par, 0x36, (par->bgr << 3));
break;
case 90:
write_reg(par, 0x36, mx | mv | (par->bgr << 3));
break;
}
return 0;
}
/*
gamma string format:
*/
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
write_reg(par, 0xE0,
curves[0], curves[1], curves[2], curves[3],
curves[4], curves[5], curves[6], curves[7],
curves[8], curves[9], curves[10], curves[11],
curves[12], curves[13], curves[14], curves[15],
curves[16], curves[17], curves[18]);
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = 128,
.height = 160,
.gamma_num = 1,
.gamma_len = 19,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "himax,hx8353d", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:hx8353d");
MODULE_ALIAS("platform:hx8353d");
MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller");
MODULE_AUTHOR("Petr Olivka");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,234 @@
/*
* FB driver for the ILI9320 LCD Controller
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ili9320"
#define WIDTH 240
#define HEIGHT 320
#define DEFAULT_GAMMA "07 07 6 0 0 0 5 5 4 0\n" \
"07 08 4 7 5 1 2 0 7 7"
static unsigned read_devicecode(struct fbtft_par *par)
{
int ret;
u8 rxbuf[8] = {0, };
write_reg(par, 0x0000);
ret = par->fbtftops.read(par, rxbuf, 4);
return (rxbuf[2] << 8) | rxbuf[3];
}
static int init_display(struct fbtft_par *par)
{
unsigned devcode;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
devcode = read_devicecode(par);
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Device code: 0x%04X\n",
devcode);
if ((devcode != 0x0000) && (devcode != 0x9320))
dev_warn(par->info->device,
"Unrecognized Device code: 0x%04X (expected 0x9320)\n",
devcode);
/* Initialization sequence from ILI9320 Application Notes */
/* *********** Start Initial Sequence ********* */
write_reg(par, 0x00E5, 0x8000); /* Set the Vcore voltage and this setting is must. */
write_reg(par, 0x0000, 0x0001); /* Start internal OSC. */
write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */
write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */
write_reg(par, 0x0004, 0x0000); /* Resize register */
write_reg(par, 0x0008, 0x0202); /* set the back and front porch */
write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */
write_reg(par, 0x000A, 0x0000); /* FMARK function */
write_reg(par, 0x000C, 0x0000); /* RGB interface setting */
write_reg(par, 0x000D, 0x0000); /* Frame marker Position */
write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */
/* ***********Power On sequence *************** */
write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */
write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */
write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */
mdelay(200); /* Dis-charge capacitor power voltage */
write_reg(par, 0x0010, 0x17B0); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
write_reg(par, 0x0011, 0x0031); /* R11h=0x0031 at VCI=3.3V DC1[2:0], DC0[2:0], VC[2:0] */
mdelay(50);
write_reg(par, 0x0012, 0x0138); /* R12h=0x0138 at VCI=3.3V VREG1OUT voltage */
mdelay(50);
write_reg(par, 0x0013, 0x1800); /* R13h=0x1800 at VCI=3.3V VDV[4:0] for VCOM amplitude */
write_reg(par, 0x0029, 0x0008); /* R29h=0x0008 at VCI=3.3V VCM[4:0] for VCOMH */
mdelay(50);
write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */
write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */
/* ------------------ Set GRAM area --------------- */
write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */
write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */
write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */
write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */
write_reg(par, 0x0060, 0x2700); /* Gate Scan Line */
write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */
write_reg(par, 0x006A, 0x0000); /* set scrolling line */
/* -------------- Partial Display Control --------- */
write_reg(par, 0x0080, 0x0000);
write_reg(par, 0x0081, 0x0000);
write_reg(par, 0x0082, 0x0000);
write_reg(par, 0x0083, 0x0000);
write_reg(par, 0x0084, 0x0000);
write_reg(par, 0x0085, 0x0000);
/* -------------- Panel Control ------------------- */
write_reg(par, 0x0090, 0x0010);
write_reg(par, 0x0092, 0x0000);
write_reg(par, 0x0093, 0x0003);
write_reg(par, 0x0095, 0x0110);
write_reg(par, 0x0097, 0x0000);
write_reg(par, 0x0098, 0x0000);
write_reg(par, 0x0007, 0x0173); /* 262K color and display ON */
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R20h = Horizontal GRAM Start Address */
/* R21h = Vertical GRAM Start Address */
case 0:
write_reg(par, 0x0020, xs);
write_reg(par, 0x0021, ys);
break;
case 180:
write_reg(par, 0x0020, WIDTH - 1 - xs);
write_reg(par, 0x0021, HEIGHT - 1 - ys);
break;
case 270:
write_reg(par, 0x0020, WIDTH - 1 - ys);
write_reg(par, 0x0021, xs);
break;
case 90:
write_reg(par, 0x0020, ys);
write_reg(par, 0x0021, HEIGHT - 1 - xs);
break;
}
write_reg(par, 0x0022); /* Write Data to GRAM */
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x3, (par->bgr << 12) | 0x30);
break;
case 270:
write_reg(par, 0x3, (par->bgr << 12) | 0x28);
break;
case 180:
write_reg(par, 0x3, (par->bgr << 12) | 0x00);
break;
case 90:
write_reg(par, 0x3, (par->bgr << 12) | 0x18);
break;
}
return 0;
}
/*
Gamma string format:
VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5
VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long mask[] = {
0b11111, 0b11111, 0b111, 0b111, 0b111,
0b111, 0b111, 0b111, 0b111, 0b111,
0b11111, 0b11111, 0b111, 0b111, 0b111,
0b111, 0b111, 0b111, 0b111, 0b111 };
int i, j;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < 2; i++)
for (j = 0; j < 10; j++)
CURVE(i, j) &= mask[i*par->gamma.num_values + j];
write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4));
write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6));
write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8));
write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2));
write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0));
write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4));
write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6));
write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8));
write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2));
write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 16,
.width = WIDTH,
.height = HEIGHT,
.gamma_num = 2,
.gamma_len = 10,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9320", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ili9320");
MODULE_ALIAS("platform:ili9320");
MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,291 @@
/*
* FB driver for the ILI9325 LCD Controller
*
* Copyright (C) 2013 Noralf Tronnes
*
* Based on ili9325.c by Jeroen Domburg
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ili9325"
#define WIDTH 240
#define HEIGHT 320
#define BPP 16
#define FPS 20
#define DEFAULT_GAMMA "0F 00 7 2 0 0 6 5 4 1\n" \
"04 16 2 7 6 3 2 1 7 7"
static unsigned bt = 6; /* VGL=Vci*4 , VGH=Vci*4 */
module_param(bt, uint, 0);
MODULE_PARM_DESC(bt, "Sets the factor used in the step-up circuits");
static unsigned vc = 0b011; /* Vci1=Vci*0.80 */
module_param(vc, uint, 0);
MODULE_PARM_DESC(vc,
"Sets the ratio factor of Vci to generate the reference voltages Vci1");
static unsigned vrh = 0b1101; /* VREG1OUT=Vci*1.85 */
module_param(vrh, uint, 0);
MODULE_PARM_DESC(vrh,
"Set the amplifying rate (1.6 ~ 1.9) of Vci applied to output the VREG1OUT");
static unsigned vdv = 0b10010; /* VCOMH amplitude=VREG1OUT*0.98 */
module_param(vdv, uint, 0);
MODULE_PARM_DESC(vdv,
"Select the factor of VREG1OUT to set the amplitude of Vcom");
static unsigned vcm = 0b001010; /* VCOMH=VREG1OUT*0.735 */
module_param(vcm, uint, 0);
MODULE_PARM_DESC(vcm, "Set the internal VcomH voltage");
/*
Verify that this configuration is within the Voltage limits
Display module configuration: Vcc = IOVcc = Vci = 3.3V
Voltages
----------
Vci = 3.3
Vci1 = Vci * 0.80 = 2.64
DDVDH = Vci1 * 2 = 5.28
VCL = -Vci1 = -2.64
VREG1OUT = Vci * 1.85 = 4.88
VCOMH = VREG1OUT * 0.735 = 3.59
VCOM amplitude = VREG1OUT * 0.98 = 4.79
VGH = Vci * 4 = 13.2
VGL = -Vci * 4 = -13.2
Limits
--------
Power supplies
1.65 < IOVcc < 3.30 => 1.65 < 3.3 < 3.30
2.40 < Vcc < 3.30 => 2.40 < 3.3 < 3.30
2.50 < Vci < 3.30 => 2.50 < 3.3 < 3.30
Source/VCOM power supply voltage
4.50 < DDVDH < 6.0 => 4.50 < 5.28 < 6.0
-3.0 < VCL < -2.0 => -3.0 < -2.64 < -2.0
VCI - VCL < 6.0 => 5.94 < 6.0
Gate driver output voltage
10 < VGH < 20 => 10 < 13.2 < 20
-15 < VGL < -5 => -15 < -13.2 < -5
VGH - VGL < 32 => 26.4 < 32
VCOM driver output voltage
VCOMH - VCOML < 6.0 => 4.79 < 6.0
*/
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
if (par->gpio.cs != -1)
gpio_set_value(par->gpio.cs, 0); /* Activate chip */
bt &= 0b111;
vc &= 0b111;
vrh &= 0b1111;
vdv &= 0b11111;
vcm &= 0b111111;
/* Initialization sequence from ILI9325 Application Notes */
/* ----------- Start Initial Sequence ----------- */
write_reg(par, 0x00E3, 0x3008); /* Set internal timing */
write_reg(par, 0x00E7, 0x0012); /* Set internal timing */
write_reg(par, 0x00EF, 0x1231); /* Set internal timing */
write_reg(par, 0x0001, 0x0100); /* set SS and SM bit */
write_reg(par, 0x0002, 0x0700); /* set 1 line inversion */
write_reg(par, 0x0004, 0x0000); /* Resize register */
write_reg(par, 0x0008, 0x0207); /* set the back porch and front porch */
write_reg(par, 0x0009, 0x0000); /* set non-display area refresh cycle */
write_reg(par, 0x000A, 0x0000); /* FMARK function */
write_reg(par, 0x000C, 0x0000); /* RGB interface setting */
write_reg(par, 0x000D, 0x0000); /* Frame marker Position */
write_reg(par, 0x000F, 0x0000); /* RGB interface polarity */
/* ----------- Power On sequence ----------- */
write_reg(par, 0x0010, 0x0000); /* SAP, BT[3:0], AP, DSTB, SLP, STB */
write_reg(par, 0x0011, 0x0007); /* DC1[2:0], DC0[2:0], VC[2:0] */
write_reg(par, 0x0012, 0x0000); /* VREG1OUT voltage */
write_reg(par, 0x0013, 0x0000); /* VDV[4:0] for VCOM amplitude */
mdelay(200); /* Dis-charge capacitor power voltage */
write_reg(par, 0x0010, /* SAP, BT[3:0], AP, DSTB, SLP, STB */
(1 << 12) | (bt << 8) | (1 << 7) | (0b001 << 4));
write_reg(par, 0x0011, 0x220 | vc); /* DC1[2:0], DC0[2:0], VC[2:0] */
mdelay(50); /* Delay 50ms */
write_reg(par, 0x0012, vrh); /* Internal reference voltage= Vci; */
mdelay(50); /* Delay 50ms */
write_reg(par, 0x0013, vdv << 8); /* Set VDV[4:0] for VCOM amplitude */
write_reg(par, 0x0029, vcm); /* Set VCM[5:0] for VCOMH */
write_reg(par, 0x002B, 0x000C); /* Set Frame Rate */
mdelay(50); /* Delay 50ms */
write_reg(par, 0x0020, 0x0000); /* GRAM horizontal Address */
write_reg(par, 0x0021, 0x0000); /* GRAM Vertical Address */
/*------------------ Set GRAM area --------------- */
write_reg(par, 0x0050, 0x0000); /* Horizontal GRAM Start Address */
write_reg(par, 0x0051, 0x00EF); /* Horizontal GRAM End Address */
write_reg(par, 0x0052, 0x0000); /* Vertical GRAM Start Address */
write_reg(par, 0x0053, 0x013F); /* Vertical GRAM Start Address */
write_reg(par, 0x0060, 0xA700); /* Gate Scan Line */
write_reg(par, 0x0061, 0x0001); /* NDL,VLE, REV */
write_reg(par, 0x006A, 0x0000); /* set scrolling line */
/*-------------- Partial Display Control --------- */
write_reg(par, 0x0080, 0x0000);
write_reg(par, 0x0081, 0x0000);
write_reg(par, 0x0082, 0x0000);
write_reg(par, 0x0083, 0x0000);
write_reg(par, 0x0084, 0x0000);
write_reg(par, 0x0085, 0x0000);
/*-------------- Panel Control ------------------- */
write_reg(par, 0x0090, 0x0010);
write_reg(par, 0x0092, 0x0600);
write_reg(par, 0x0007, 0x0133); /* 262K color and display ON */
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R20h = Horizontal GRAM Start Address */
/* R21h = Vertical GRAM Start Address */
case 0:
write_reg(par, 0x0020, xs);
write_reg(par, 0x0021, ys);
break;
case 180:
write_reg(par, 0x0020, WIDTH - 1 - xs);
write_reg(par, 0x0021, HEIGHT - 1 - ys);
break;
case 270:
write_reg(par, 0x0020, WIDTH - 1 - ys);
write_reg(par, 0x0021, xs);
break;
case 90:
write_reg(par, 0x0020, ys);
write_reg(par, 0x0021, HEIGHT - 1 - xs);
break;
}
write_reg(par, 0x0022); /* Write Data to GRAM */
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
/* AM: GRAM update direction */
case 0:
write_reg(par, 0x03, 0x0030 | (par->bgr << 12));
break;
case 180:
write_reg(par, 0x03, 0x0000 | (par->bgr << 12));
break;
case 270:
write_reg(par, 0x03, 0x0028 | (par->bgr << 12));
break;
case 90:
write_reg(par, 0x03, 0x0018 | (par->bgr << 12));
break;
}
return 0;
}
/*
Gamma string format:
VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5
VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long mask[] = {
0b11111, 0b11111, 0b111, 0b111, 0b111,
0b111, 0b111, 0b111, 0b111, 0b111,
0b11111, 0b11111, 0b111, 0b111, 0b111,
0b111, 0b111, 0b111, 0b111, 0b111 };
int i, j;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < 2; i++)
for (j = 0; j < 10; j++)
CURVE(i, j) &= mask[i*par->gamma.num_values + j];
write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4));
write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6));
write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8));
write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2));
write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0));
write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4));
write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6));
write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8));
write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2));
write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 16,
.width = WIDTH,
.height = HEIGHT,
.bpp = BPP,
.fps = FPS,
.gamma_num = 2,
.gamma_len = 10,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9325", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ili9325");
MODULE_ALIAS("platform:ili9325");
MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,163 @@
/*
* FB driver for the ILI9340 LCD Controller
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ili9340"
#define WIDTH 240
#define HEIGHT 320
/* Init sequence taken from: Arduino Library for the Adafruit 2.2" display */
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
write_reg(par, 0xEF, 0x03, 0x80, 0x02);
write_reg(par, 0xCF, 0x00 , 0XC1 , 0X30);
write_reg(par, 0xED, 0x64 , 0x03 , 0X12 , 0X81);
write_reg(par, 0xE8, 0x85 , 0x00 , 0x78);
write_reg(par, 0xCB, 0x39 , 0x2C , 0x00 , 0x34 , 0x02);
write_reg(par, 0xF7, 0x20);
write_reg(par, 0xEA, 0x00 , 0x00);
/* Power Control 1 */
write_reg(par, 0xC0, 0x23);
/* Power Control 2 */
write_reg(par, 0xC1, 0x10);
/* VCOM Control 1 */
write_reg(par, 0xC5, 0x3e, 0x28);
/* VCOM Control 2 */
write_reg(par, 0xC7, 0x86);
/* COLMOD: Pixel Format Set */
/* 16 bits/pixel */
write_reg(par, 0x3A, 0x55);
/* Frame Rate Control */
/* Division ratio = fosc, Frame Rate = 79Hz */
write_reg(par, 0xB1, 0x00, 0x18);
/* Display Function Control */
write_reg(par, 0xB6, 0x08, 0x82, 0x27);
/* Gamma Function Disable */
write_reg(par, 0xF2, 0x00);
/* Gamma curve selected */
write_reg(par, 0x26, 0x01);
/* Positive Gamma Correction */
write_reg(par, 0xE0,
0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1,
0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00);
/* Negative Gamma Correction */
write_reg(par, 0xE1,
0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1,
0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F);
/* Sleep OUT */
write_reg(par, 0x11);
mdelay(120);
/* Display ON */
write_reg(par, 0x29);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address */
write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
/* Row adress */
write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
/* Memory write */
write_reg(par, 0x2C);
}
#define ILI9340_MADCTL_MV 0x20
#define ILI9340_MADCTL_MX 0x40
#define ILI9340_MADCTL_MY 0x80
static int set_var(struct fbtft_par *par)
{
u8 val;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
case 270:
val = ILI9340_MADCTL_MV;
break;
case 180:
val = ILI9340_MADCTL_MY;
break;
case 90:
val = ILI9340_MADCTL_MV | ILI9340_MADCTL_MY | ILI9340_MADCTL_MX;
break;
default:
val = ILI9340_MADCTL_MX;
break;
}
/* Memory Access Control */
write_reg(par, 0x36, val | (par->bgr << 3));
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9340", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ili9340");
MODULE_ALIAS("platform:ili9340");
MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,179 @@
/*
* FB driver for the ILI9341 LCD display controller
*
* This display uses 9-bit SPI: Data/Command bit + 8 data bits
* For platforms that doesn't support 9-bit, the driver is capable
* of emulating this using 8-bit transfer.
* This is done by transfering eight 9-bit words in 9 bytes.
*
* Copyright (C) 2013 Christian Vogelgsang
* Based on adafruit22fb.c by 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/init.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ili9341"
#define WIDTH 240
#define HEIGHT 320
#define TXBUFLEN (4 * PAGE_SIZE)
#define DEFAULT_GAMMA "1F 1A 18 0A 0F 06 45 87 32 0A 07 02 07 05 00\n" \
"00 25 27 05 10 09 3A 78 4D 05 18 0D 38 3A 1F"
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
/* startup sequence for MI0283QT-9A */
write_reg(par, 0x01); /* software reset */
mdelay(5);
write_reg(par, 0x28); /* display off */
/* --------------------------------------------------------- */
write_reg(par, 0xCF, 0x00, 0x83, 0x30);
write_reg(par, 0xED, 0x64, 0x03, 0x12, 0x81);
write_reg(par, 0xE8, 0x85, 0x01, 0x79);
write_reg(par, 0xCB, 0x39, 0X2C, 0x00, 0x34, 0x02);
write_reg(par, 0xF7, 0x20);
write_reg(par, 0xEA, 0x00, 0x00);
/* ------------power control-------------------------------- */
write_reg(par, 0xC0, 0x26);
write_reg(par, 0xC1, 0x11);
/* ------------VCOM --------- */
write_reg(par, 0xC5, 0x35, 0x3E);
write_reg(par, 0xC7, 0xBE);
/* ------------memory access control------------------------ */
write_reg(par, 0x3A, 0x55); /* 16bit pixel */
/* ------------frame rate----------------------------------- */
write_reg(par, 0xB1, 0x00, 0x1B);
/* ------------Gamma---------------------------------------- */
/* write_reg(par, 0xF2, 0x08); */ /* Gamma Function Disable */
write_reg(par, 0x26, 0x01);
/* ------------display-------------------------------------- */
write_reg(par, 0xB7, 0x07); /* entry mode set */
write_reg(par, 0xB6, 0x0A, 0x82, 0x27, 0x00);
write_reg(par, 0x11); /* sleep out */
mdelay(100);
write_reg(par, 0x29); /* display on */
mdelay(20);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address set */
write_reg(par, 0x2A,
(xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
/* Row adress set */
write_reg(par, 0x2B,
(ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
/* Memory write */
write_reg(par, 0x2C);
}
#define MEM_Y (7) /* MY row address order */
#define MEM_X (6) /* MX column address order */
#define MEM_V (5) /* MV row / column exchange */
#define MEM_L (4) /* ML vertical refresh order */
#define MEM_H (2) /* MH horizontal refresh order */
#define MEM_BGR (3) /* RGB-BGR Order */
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x36, (1 << MEM_X) | (par->bgr << MEM_BGR));
break;
case 270:
write_reg(par, 0x36,
(1<<MEM_V) | (1 << MEM_L) | (par->bgr << MEM_BGR));
break;
case 180:
write_reg(par, 0x36, (1 << MEM_Y) | (par->bgr << MEM_BGR));
break;
case 90:
write_reg(par, 0x36, (1 << MEM_Y) | (1 << MEM_X) |
(1 << MEM_V) | (par->bgr << MEM_BGR));
break;
}
return 0;
}
/*
Gamma string format:
Positive: Par1 Par2 [...] Par15
Negative: Par1 Par2 [...] Par15
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
int i;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
for (i = 0; i < par->gamma.num_curves; i++)
write_reg(par, 0xE0 + i,
CURVE(i, 0), CURVE(i, 1), CURVE(i, 2),
CURVE(i, 3), CURVE(i, 4), CURVE(i, 5),
CURVE(i, 6), CURVE(i, 7), CURVE(i, 8),
CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
CURVE(i, 12), CURVE(i, 13), CURVE(i, 14));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.txbuflen = TXBUFLEN,
.gamma_num = 2,
.gamma_len = 15,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9341", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ili9341");
MODULE_ALIAS("platform:ili9341");
MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller");
MODULE_AUTHOR("Christian Vogelgsang");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,117 @@
/*
* FB driver for the ILI9481 LCD Controller
*
* Copyright (c) 2014 Petr Olivka
* 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/init.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ili9481"
#define WIDTH 320
#define HEIGHT 480
static int default_init_sequence[] = {
/* SLP_OUT - Sleep out */
-1, 0x11,
-2, 50,
/* Power setting */
-1, 0xD0, 0x07, 0x42, 0x18,
/* VCOM */
-1, 0xD1, 0x00, 0x07, 0x10,
/* Power setting for norm. mode */
-1, 0xD2, 0x01, 0x02,
/* Panel driving setting */
-1, 0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11,
/* Frame rate & inv. */
-1, 0xC5, 0x03,
/* Pixel format */
-1, 0x3A, 0x55,
/* Gamma */
-1, 0xC8, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16,
0x37, 0x75, 0x77, 0x54, 0x0C, 0x00,
/* DISP_ON */
-1, 0x29,
-3
};
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* column address */
write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
/* row adress */
write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
/* memory write */
write_reg(par, 0x2c);
}
#define HFLIP 0x01
#define VFLIP 0x02
#define ROWxCOL 0x20
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
case 270:
write_reg(par, 0x36, ROWxCOL | HFLIP | VFLIP | (par->bgr << 3));
break;
case 180:
write_reg(par, 0x36, VFLIP | (par->bgr << 3));
break;
case 90:
write_reg(par, 0x36, ROWxCOL | (par->bgr << 3));
break;
default:
write_reg(par, 0x36, HFLIP | (par->bgr << 3));
break;
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.init_sequence = default_init_sequence,
.fbtftops = {
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9481", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ili9481");
MODULE_ALIAS("platform:ili9481");
MODULE_DESCRIPTION("FB driver for the ILI9481 LCD Controller");
MODULE_AUTHOR("Petr Olivka");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,121 @@
/*
* FB driver for the ILI9486 LCD Controller
*
* Copyright (C) 2014 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/init.h>
#include "fbtft.h"
#define DRVNAME "fb_ili9486"
#define WIDTH 320
#define HEIGHT 480
/* this init sequence matches PiScreen */
static int default_init_sequence[] = {
/* Interface Mode Control */
-1, 0xb0, 0x0,
/* Sleep OUT */
-1, 0x11,
-2, 250,
/* Interface Pixel Format */
-1, 0x3A, 0x55,
/* Power Control 3 */
-1, 0xC2, 0x44,
/* VCOM Control 1 */
-1, 0xC5, 0x00, 0x00, 0x00, 0x00,
/* PGAMCTRL(Positive Gamma Control) */
-1, 0xE0, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98,
0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00,
/* NGAMCTRL(Negative Gamma Control) */
-1, 0xE1, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75,
0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
/* Digital Gamma Control 1 */
-1, 0xE2, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75,
0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,
/* Sleep OUT */
-1, 0x11,
/* Display ON */
-1, 0x29,
/* end marker */
-3
};
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address */
write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
/* Row adress */
write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
/* Memory write */
write_reg(par, 0x2C);
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x36, 0x80 | (par->bgr << 3));
break;
case 90:
write_reg(par, 0x36, 0x20 | (par->bgr << 3));
break;
case 180:
write_reg(par, 0x36, 0x40 | (par->bgr << 3));
break;
case 270:
write_reg(par, 0x36, 0xE0 | (par->bgr << 3));
break;
default:
break;
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.init_sequence = default_init_sequence,
.fbtftops = {
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9486", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ili9486");
MODULE_ALIAS("platform:ili9486");
MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,177 @@
/*
* FB driver for the PCD8544 LCD Controller
*
* The display is monochrome and the video memory is RGB565.
* Any pixel value except 0 turns the pixel on.
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_pcd8544"
#define WIDTH 84
#define HEIGHT 48
#define TXBUFLEN 84*6
#define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */
static unsigned tc = 0;
module_param(tc, uint, 0);
MODULE_PARM_DESC(tc, "TC[1:0] Temperature coefficient: 0-3 (default: 0)");
static unsigned bs = 4;
module_param(bs, uint, 0);
MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)");
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
/* Function set */
write_reg(par, 0x21); /* 5:1 1
2:0 PD - Powerdown control: chip is active
1:0 V - Entry mode: horizontal addressing
0:1 H - Extended instruction set control: extended
*/
/* H=1 Temperature control */
write_reg(par, 0x04 | (tc & 0x3)); /*
2:1 1
1:x TC1 - Temperature Coefficient: 0x10
0:x TC0
*/
/* H=1 Bias system */
write_reg(par, 0x10 | (bs & 0x7)); /*
4:1 1
3:0 0
2:x BS2 - Bias System
1:x BS1
0:x BS0
*/
/* Function set */
write_reg(par, 0x22); /* 5:1 1
2:0 PD - Powerdown control: chip is active
1:1 V - Entry mode: vertical addressing
0:0 H - Extended instruction set control: basic
*/
/* H=0 Display control */
write_reg(par, 0x08 | 4); /*
3:1 1
2:1 D - DE: 10=normal mode
1:0 0
0:0 E
*/
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* H=0 Set X address of RAM */
write_reg(par, 0x80); /* 7:1 1
6-0: X[6:0] - 0x00
*/
/* H=0 Set Y address of RAM */
write_reg(par, 0x40); /* 7:0 0
6:1 1
2-0: Y[2:0] - 0x0
*/
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16 = (u16 *)par->info->screen_base;
u8 *buf = par->txbuf.buf;
int x, y, i;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
for (x=0;x<84;x++) {
for (y=0;y<6;y++) {
*buf = 0x00;
for (i=0;i<8;i++) {
*buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i;
}
buf++;
}
}
/* Write data */
gpio_set_value(par->gpio.dc, 1);
ret = par->fbtftops.write(par, par->txbuf.buf, 6*84);
if (ret < 0)
dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret);
return ret;
}
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
curves[0] &= 0x7F;
write_reg(par, 0x23); /* turn on extended instruction set */
write_reg(par, 0x80 | curves[0]);
write_reg(par, 0x22); /* turn off extended instruction set */
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.txbuflen = TXBUFLEN,
.gamma_num = 1,
.gamma_len = 1,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.write_vmem = write_vmem,
.set_gamma = set_gamma,
},
.backlight = 1,
};
FBTFT_REGISTER_DRIVER(DRVNAME, "philips,pdc8544", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("spi:pdc8544");
MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,331 @@
/******************************************************************************
ProjectName: FBTFT driver ***** *****
for the RA8875 LCD Controller * * ************
* ** ** * *
Copyright <20> by Pf@nne & NOTRO * * * * * **** *
* * * * * * *
Last modification by: * * * * **** *
- Pf@nne (pf@nne-mail.de) * * ***** *
* * * *******
***** * *
Date : 10.06.2014 * *
Version : V1.13 *****
Revison : 5
*******************************************************************************
* 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/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include "fbtft.h"
#define DRVNAME "fb_ra8875"
static int write_spi(struct fbtft_par *par, void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
.speed_hz = 1000000,
};
struct spi_message m;
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
if (!par->spi) {
dev_err(par->info->device,
"%s: par->spi is unexpectedly NULL\n", __func__);
return -1;
}
spi_message_init(&m);
if (par->txbuf.dma && buf == par->txbuf.buf) {
t.tx_dma = par->txbuf.dma;
m.is_dma_mapped = 1;
}
spi_message_add_tail(&t, &m);
return spi_sync(par->spi, &m);
}
static int init_display(struct fbtft_par *par)
{
gpio_set_value(par->gpio.dc, 1);
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
"%s()\n", __func__);
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
"display size %dx%d\n", par->info->var.xres, par->info->var.yres);
par->fbtftops.reset(par);
if ((par->info->var.xres == 320) && (par->info->var.yres == 240)) {
/* PLL clock frequency */
write_reg(par, 0x88 , 0x0A);
write_reg(par, 0x89 , 0x02);
mdelay(10);
/* color deep / MCU Interface */
write_reg(par, 0x10 , 0x0C);
/* pixel clock period */
write_reg(par, 0x04 , 0x03);
mdelay(1);
/* horizontal settings */
write_reg(par, 0x14 , 0x27);
write_reg(par, 0x15 , 0x00);
write_reg(par, 0x16 , 0x05);
write_reg(par, 0x17 , 0x04);
write_reg(par, 0x18 , 0x03);
/* vertical settings */
write_reg(par, 0x19 , 0xEF);
write_reg(par, 0x1A , 0x00);
write_reg(par, 0x1B , 0x05);
write_reg(par, 0x1C , 0x00);
write_reg(par, 0x1D , 0x0E);
write_reg(par, 0x1E , 0x00);
write_reg(par, 0x1F , 0x02);
} else if ((par->info->var.xres == 480) && (par->info->var.yres == 272)) {
/* PLL clock frequency */
write_reg(par, 0x88 , 0x0A);
write_reg(par, 0x89 , 0x02);
mdelay(10);
/* color deep / MCU Interface */
write_reg(par, 0x10 , 0x0C);
/* pixel clock period */
write_reg(par, 0x04 , 0x82);
mdelay(1);
/* horizontal settings */
write_reg(par, 0x14 , 0x3B);
write_reg(par, 0x15 , 0x00);
write_reg(par, 0x16 , 0x01);
write_reg(par, 0x17 , 0x00);
write_reg(par, 0x18 , 0x05);
/* vertical settings */
write_reg(par, 0x19 , 0x0F);
write_reg(par, 0x1A , 0x01);
write_reg(par, 0x1B , 0x02);
write_reg(par, 0x1C , 0x00);
write_reg(par, 0x1D , 0x07);
write_reg(par, 0x1E , 0x00);
write_reg(par, 0x1F , 0x09);
} else if ((par->info->var.xres == 640) && (par->info->var.yres == 480)) {
/* PLL clock frequency */
write_reg(par, 0x88 , 0x0B);
write_reg(par, 0x89 , 0x02);
mdelay(10);
/* color deep / MCU Interface */
write_reg(par, 0x10 , 0x0C);
/* pixel clock period */
write_reg(par, 0x04 , 0x01);
mdelay(1);
/* horizontal settings */
write_reg(par, 0x14 , 0x4F);
write_reg(par, 0x15 , 0x05);
write_reg(par, 0x16 , 0x0F);
write_reg(par, 0x17 , 0x01);
write_reg(par, 0x18 , 0x00);
/* vertical settings */
write_reg(par, 0x19 , 0xDF);
write_reg(par, 0x1A , 0x01);
write_reg(par, 0x1B , 0x0A);
write_reg(par, 0x1C , 0x00);
write_reg(par, 0x1D , 0x0E);
write_reg(par, 0x1E , 0x00);
write_reg(par, 0x1F , 0x01);
} else if ((par->info->var.xres == 800) && (par->info->var.yres == 480)) {
/* PLL clock frequency */
write_reg(par, 0x88 , 0x0B);
write_reg(par, 0x89 , 0x02);
mdelay(10);
/* color deep / MCU Interface */
write_reg(par, 0x10 , 0x0C);
/* pixel clock period */
write_reg(par, 0x04 , 0x81);
mdelay(1);
/* horizontal settings */
write_reg(par, 0x14 , 0x63);
write_reg(par, 0x15 , 0x03);
write_reg(par, 0x16 , 0x03);
write_reg(par, 0x17 , 0x02);
write_reg(par, 0x18 , 0x00);
/* vertical settings */
write_reg(par, 0x19 , 0xDF);
write_reg(par, 0x1A , 0x01);
write_reg(par, 0x1B , 0x14);
write_reg(par, 0x1C , 0x00);
write_reg(par, 0x1D , 0x06);
write_reg(par, 0x1E , 0x00);
write_reg(par, 0x1F , 0x01);
} else {
dev_err(par->info->device, "display size is not supported!!");
return -1;
}
/* PWM clock */
write_reg(par, 0x8a , 0x81);
write_reg(par, 0x8b , 0xFF);
mdelay(10);
/* Display ON */
write_reg(par, 0x01 , 0x80);
mdelay(10);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Set_Active_Window */
write_reg(par, 0x30 , xs & 0x00FF);
write_reg(par, 0x31 , (xs & 0xFF00) >> 8);
write_reg(par, 0x32 , ys & 0x00FF);
write_reg(par, 0x33 , (ys & 0xFF00) >> 8);
write_reg(par, 0x34 , (xs+xe) & 0x00FF);
write_reg(par, 0x35 , ((xs+xe) & 0xFF00) >> 8);
write_reg(par, 0x36 , (ys+ye) & 0x00FF);
write_reg(par, 0x37 , ((ys+ye) & 0xFF00) >> 8);
/* Set_Memory_Write_Cursor */
write_reg(par, 0x46, xs & 0xff);
write_reg(par, 0x47, (xs >> 8) & 0x03);
write_reg(par, 0x48, ys & 0xff);
write_reg(par, 0x49, (ys >> 8) & 0x01);
write_reg(par, 0x02);
}
static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
va_list args;
int i, ret;
u8 *buf = (u8 *)par->buf;
/* slow down spi-speed for writing registers */
par->fbtftops.write = write_spi;
if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
va_start(args, len);
for (i = 0; i < len; i++)
buf[i] = (u8)va_arg(args, unsigned int);
va_end(args);
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device,
u8, buf, len, "%s: ", __func__);
}
va_start(args, len);
*buf++ = 0x80;
*buf = (u8)va_arg(args, unsigned int);
ret = par->fbtftops.write(par, par->buf, 2);
if (ret < 0) {
va_end(args);
dev_err(par->info->device, "%s: write() failed and returned %dn",
__func__, ret);
return;
}
len--;
udelay(100);
if (len) {
buf = (u8 *)par->buf;
*buf++ = 0x00;
i = len;
while (i--)
*buf++ = (u8)va_arg(args, unsigned int);
ret = par->fbtftops.write(par, par->buf, len + 1);
if (ret < 0) {
va_end(args);
dev_err(par->info->device, "%s: write() failed and returned %dn",
__func__, ret);
return;
}
}
va_end(args);
/* restore user spi-speed */
par->fbtftops.write = fbtft_write_spi;
udelay(100);
}
static int write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16;
u16 *txbuf16 = (u16 *)par->txbuf.buf;
size_t remain;
size_t to_copy;
size_t tx_array_size;
int i;
int ret = 0;
size_t startbyte_size = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
__func__, offset, len);
remain = len / 2;
vmem16 = (u16 *)(par->info->screen_base + offset);
tx_array_size = par->txbuf.len / 2;
txbuf16 = (u16 *)(par->txbuf.buf + 1);
tx_array_size -= 2;
*(u8 *)(par->txbuf.buf) = 0x00;
startbyte_size = 1;
while (remain) {
to_copy = remain > tx_array_size ? tx_array_size : remain;
dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n",
to_copy, remain - to_copy);
for (i = 0; i < to_copy; i++)
txbuf16[i] = cpu_to_be16(vmem16[i]);
vmem16 = vmem16 + to_copy;
ret = par->fbtftops.write(par, par->txbuf.buf,
startbyte_size + to_copy * 2);
if (ret < 0)
return ret;
remain -= to_copy;
}
return ret;
}
static struct fbtft_display display = {
.regwidth = 8,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.write_register = write_reg8_bus8,
.write_vmem = write_vmem16_bus8,
.write = write_spi,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "raio,ra8875", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ra8875");
MODULE_ALIAS("platform:ra8875");
MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller");
MODULE_AUTHOR("Pf@nne");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,168 @@
/*
* FB driver for the S6D02A1 LCD Controller
*
* Based on fb_st7735r.c by Noralf Tronnes
* Init code from UTFT library by Henning Karlsen
*
* 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/init.h>
#include "fbtft.h"
#define DRVNAME "fb_s6d02a1"
static int default_init_sequence[] = {
-1, 0xf0, 0x5a, 0x5a,
-1, 0xfc, 0x5a, 0x5a,
-1, 0xfa, 0x02, 0x1f, 0x00, 0x10, 0x22, 0x30, 0x38, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3d, 0x02, 0x01,
-1, 0xfb, 0x21, 0x00, 0x02, 0x04, 0x07, 0x0a, 0x0b, 0x0c, 0x0c, 0x16, 0x1e, 0x30, 0x3f, 0x01, 0x02,
/* power setting sequence */
-1, 0xfd, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x01, 0x01, 0x00, 0x1f, 0x1f,
-1, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00,
-1, 0xf5, 0x00, 0x70, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x66, 0x06,
-1, 0xf6, 0x02, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x02, 0x00, 0x06, 0x01, 0x00,
-1, 0xf2, 0x00, 0x01, 0x03, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x08, 0x08,
-1, 0xf8, 0x11,
-1, 0xf7, 0xc8, 0x20, 0x00, 0x00,
-1, 0xf3, 0x00, 0x00,
-1, 0x11,
-2, 50,
-1, 0xf3, 0x00, 0x01,
-2, 50,
-1, 0xf3, 0x00, 0x03,
-2, 50,
-1, 0xf3, 0x00, 0x07,
-2, 50,
-1, 0xf3, 0x00, 0x0f,
-2, 50,
-1, 0xf4, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00,
-2, 50,
-1, 0xf3, 0x00, 0x1f,
-2, 50,
-1, 0xf3, 0x00, 0x7f,
-2, 50,
-1, 0xf3, 0x00, 0xff,
-2, 50,
-1, 0xfd, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x01, 0x00, 0x16, 0x16,
-1, 0xf4, 0x00, 0x09, 0x00, 0x00, 0x00, 0x3f, 0x3f, 0x07, 0x00, 0x3C, 0x36, 0x00, 0x3C, 0x36, 0x00,
/* initializing sequence */
-1, 0x36, 0x08,
-1, 0x35, 0x00,
-1, 0x3a, 0x05,
/* gamma setting sequence */
-1, 0x26, 0x01, /* preset gamma curves, possible values 0x01, 0x02, 0x04, 0x08 */
-2, 150,
-1, 0x29,
-1, 0x2c,
/* end marker */
-3
};
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address */
write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
/* Row adress */
write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
/* Memory write */
write_reg(par, 0x2C);
}
#define MY (1 << 7)
#define MX (1 << 6)
#define MV (1 << 5)
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* MADCTL - Memory data access control
RGB/BGR:
1. Mode selection pin SRGB
RGB H/W pin for color filter setting: 0=RGB, 1=BGR
2. MADCTL RGB bit
RGB-BGR ORDER color filter panel: 0=RGB, 1=BGR */
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x36, MX | MY | (par->bgr << 3));
break;
case 270:
write_reg(par, 0x36, MY | MV | (par->bgr << 3));
break;
case 180:
write_reg(par, 0x36, (par->bgr << 3));
break;
case 90:
write_reg(par, 0x36, MX | MV | (par->bgr << 3));
break;
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = 128,
.height = 160,
.init_sequence = default_init_sequence,
.fbtftops = {
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d02a1", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:s6d02a1");
MODULE_ALIAS("platform:s6d02a1");
MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller");
MODULE_AUTHOR("WOLFGANG BUENING");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,208 @@
/*
* FB driver for the S6D1121 LCD Controller
*
* Copyright (C) 2013 Roman Rolinsky
*
* Based on fb_ili9325.c by Noralf Tronnes
* Based on ili9325.c by Jeroen Domburg
* Init code from UTFT library by Henning Karlsen
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_s6d1121"
#define WIDTH 240
#define HEIGHT 320
#define BPP 16
#define FPS 20
#define DEFAULT_GAMMA "26 09 24 2C 1F 23 24 25 22 26 25 23 0D 00\n" \
"1C 1A 13 1D 0B 11 12 10 13 15 36 19 00 0D"
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
if (par->gpio.cs != -1)
gpio_set_value(par->gpio.cs, 0); /* Activate chip */
/* Initialization sequence from Lib_UTFT */
write_reg(par, 0x0011, 0x2004);
write_reg(par, 0x0013, 0xCC00);
write_reg(par, 0x0015, 0x2600);
write_reg(par, 0x0014, 0x252A);
write_reg(par, 0x0012, 0x0033);
write_reg(par, 0x0013, 0xCC04);
write_reg(par, 0x0013, 0xCC06);
write_reg(par, 0x0013, 0xCC4F);
write_reg(par, 0x0013, 0x674F);
write_reg(par, 0x0011, 0x2003);
write_reg(par, 0x0016, 0x0007);
write_reg(par, 0x0002, 0x0013);
write_reg(par, 0x0003, 0x0003);
write_reg(par, 0x0001, 0x0127);
write_reg(par, 0x0008, 0x0303);
write_reg(par, 0x000A, 0x000B);
write_reg(par, 0x000B, 0x0003);
write_reg(par, 0x000C, 0x0000);
write_reg(par, 0x0041, 0x0000);
write_reg(par, 0x0050, 0x0000);
write_reg(par, 0x0060, 0x0005);
write_reg(par, 0x0070, 0x000B);
write_reg(par, 0x0071, 0x0000);
write_reg(par, 0x0078, 0x0000);
write_reg(par, 0x007A, 0x0000);
write_reg(par, 0x0079, 0x0007);
write_reg(par, 0x0007, 0x0051);
write_reg(par, 0x0007, 0x0053);
write_reg(par, 0x0079, 0x0000);
write_reg(par, 0x0022); /* Write Data to GRAM */
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R20h = Horizontal GRAM Start Address */
/* R21h = Vertical GRAM Start Address */
case 0:
write_reg(par, 0x0020, xs);
write_reg(par, 0x0021, ys);
break;
case 180:
write_reg(par, 0x0020, WIDTH - 1 - xs);
write_reg(par, 0x0021, HEIGHT - 1 - ys);
break;
case 270:
write_reg(par, 0x0020, WIDTH - 1 - ys);
write_reg(par, 0x0021, xs);
break;
case 90:
write_reg(par, 0x0020, ys);
write_reg(par, 0x0021, HEIGHT - 1 - xs);
break;
}
write_reg(par, 0x0022); /* Write Data to GRAM */
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
/* AM: GRAM update direction */
case 0:
write_reg(par, 0x03, 0x0003 | (par->bgr << 12));
break;
case 180:
write_reg(par, 0x03, 0x0000 | (par->bgr << 12));
break;
case 270:
write_reg(par, 0x03, 0x000A | (par->bgr << 12));
break;
case 90:
write_reg(par, 0x03, 0x0009 | (par->bgr << 12));
break;
}
return 0;
}
/*
Gamma string format:
PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 PKP6 PKP7 PKP8 PKP9 PKP10 PKP11 VRP0 VRP1
PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long mask[] = {
0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111,
0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111,
0b11111, 0b11111,
0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111,
0b111111, 0b111111, 0b111111, 0b111111, 0b111111, 0b111111,
0b11111, 0b11111 };
int i, j;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < 2; i++)
for (j = 0; j < 14; j++)
CURVE(i, j) &= mask[i*par->gamma.num_values + j];
write_reg(par, 0x0030, CURVE(0, 1) << 8 | CURVE(0, 0));
write_reg(par, 0x0031, CURVE(0, 3) << 8 | CURVE(0, 2));
write_reg(par, 0x0032, CURVE(0, 5) << 8 | CURVE(0, 3));
write_reg(par, 0x0033, CURVE(0, 7) << 8 | CURVE(0, 6));
write_reg(par, 0x0034, CURVE(0, 9) << 8 | CURVE(0, 8));
write_reg(par, 0x0035, CURVE(0, 11) << 8 | CURVE(0, 10));
write_reg(par, 0x0036, CURVE(1, 1) << 8 | CURVE(1, 0));
write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2));
write_reg(par, 0x0038, CURVE(1, 5) << 8 | CURVE(1, 4));
write_reg(par, 0x0039, CURVE(1, 7) << 8 | CURVE(1, 6));
write_reg(par, 0x003A, CURVE(1, 9) << 8 | CURVE(1, 8));
write_reg(par, 0x003B, CURVE(1, 11) << 8 | CURVE(1, 10));
write_reg(par, 0x003C, CURVE(0, 13) << 8 | CURVE(0, 12));
write_reg(par, 0x003D, CURVE(1, 13) << 8 | CURVE(1, 12));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 16,
.width = WIDTH,
.height = HEIGHT,
.bpp = BPP,
.fps = FPS,
.gamma_num = 2,
.gamma_len = 14,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d1121", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:s6d1121");
MODULE_ALIAS("platform:s6d1121");
MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller");
MODULE_AUTHOR("Roman Rolinsky");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,206 @@
/*
* FB driver for the SSD1289 LCD Controller
*
* Copyright (C) 2013 Noralf Tronnes
*
* Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen
*
* 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/init.h>
#include <linux/gpio.h>
#include "fbtft.h"
#define DRVNAME "fb_ssd1289"
#define WIDTH 240
#define HEIGHT 320
#define DEFAULT_GAMMA "02 03 2 5 7 7 4 2 4 2\n" \
"02 03 2 5 7 5 4 2 4 2"
static unsigned reg11 = 0x6040;
module_param(reg11, uint, 0);
MODULE_PARM_DESC(reg11, "Register 11h value");
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
if (par->gpio.cs != -1)
gpio_set_value(par->gpio.cs, 0); /* Activate chip */
write_reg(par, 0x00, 0x0001);
write_reg(par, 0x03, 0xA8A4);
write_reg(par, 0x0C, 0x0000);
write_reg(par, 0x0D, 0x080C);
write_reg(par, 0x0E, 0x2B00);
write_reg(par, 0x1E, 0x00B7);
write_reg(par, 0x01,
(1 << 13) | (par->bgr << 11) | (1 << 9) | (HEIGHT - 1));
write_reg(par, 0x02, 0x0600);
write_reg(par, 0x10, 0x0000);
write_reg(par, 0x05, 0x0000);
write_reg(par, 0x06, 0x0000);
write_reg(par, 0x16, 0xEF1C);
write_reg(par, 0x17, 0x0003);
write_reg(par, 0x07, 0x0233);
write_reg(par, 0x0B, 0x0000);
write_reg(par, 0x0F, 0x0000);
write_reg(par, 0x41, 0x0000);
write_reg(par, 0x42, 0x0000);
write_reg(par, 0x48, 0x0000);
write_reg(par, 0x49, 0x013F);
write_reg(par, 0x4A, 0x0000);
write_reg(par, 0x4B, 0x0000);
write_reg(par, 0x44, 0xEF00);
write_reg(par, 0x45, 0x0000);
write_reg(par, 0x46, 0x013F);
write_reg(par, 0x23, 0x0000);
write_reg(par, 0x24, 0x0000);
write_reg(par, 0x25, 0x8000);
write_reg(par, 0x4f, 0x0000);
write_reg(par, 0x4e, 0x0000);
write_reg(par, 0x22);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R4Eh - Set GDDRAM X address counter */
/* R4Fh - Set GDDRAM Y address counter */
case 0:
write_reg(par, 0x4e, xs);
write_reg(par, 0x4f, ys);
break;
case 180:
write_reg(par, 0x4e, par->info->var.xres - 1 - xs);
write_reg(par, 0x4f, par->info->var.yres - 1 - ys);
break;
case 270:
write_reg(par, 0x4e, par->info->var.yres - 1 - ys);
write_reg(par, 0x4f, xs);
break;
case 90:
write_reg(par, 0x4e, ys);
write_reg(par, 0x4f, par->info->var.xres - 1 - xs);
break;
}
/* R22h - RAM data write */
write_reg(par, 0x22);
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
if (par->fbtftops.init_display != init_display) {
/* don't risk messing up register 11h */
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
"%s: skipping since custom init_display() is used\n",
__func__);
return 0;
}
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x11, reg11 | 0b110000);
break;
case 270:
write_reg(par, 0x11, reg11 | 0b101000);
break;
case 180:
write_reg(par, 0x11, reg11 | 0b000000);
break;
case 90:
write_reg(par, 0x11, reg11 | 0b011000);
break;
}
return 0;
}
/*
Gamma string format:
VRP0 VRP1 PRP0 PRP1 PKP0 PKP1 PKP2 PKP3 PKP4 PKP5
VRN0 VRN1 PRN0 PRN1 PKN0 PKN1 PKN2 PKN3 PKN4 PKN5
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long mask[] = {
0b11111, 0b11111, 0b111, 0b111, 0b111,
0b111, 0b111, 0b111, 0b111, 0b111,
0b11111, 0b11111, 0b111, 0b111, 0b111,
0b111, 0b111, 0b111, 0b111, 0b111 };
int i, j;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < 2; i++)
for (j = 0; j < 10; j++)
CURVE(i, j) &= mask[i*par->gamma.num_values + j];
write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4));
write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6));
write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8));
write_reg(par, 0x0033, CURVE(0, 3) << 8 | CURVE(0, 2));
write_reg(par, 0x0034, CURVE(1, 5) << 8 | CURVE(1, 4));
write_reg(par, 0x0035, CURVE(1, 7) << 8 | CURVE(1, 6));
write_reg(par, 0x0036, CURVE(1, 9) << 8 | CURVE(1, 8));
write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2));
write_reg(par, 0x003A, CURVE(0, 1) << 8 | CURVE(0, 0));
write_reg(par, 0x003B, CURVE(1, 1) << 8 | CURVE(1, 0));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 16,
.width = WIDTH,
.height = HEIGHT,
.gamma_num = 2,
.gamma_len = 10,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1289", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ssd1289");
MODULE_ALIAS("platform:ssd1289");
MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,229 @@
/*
* FB driver for the SSD1306 OLED Controller
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ssd1306"
#define WIDTH 128
#define HEIGHT 64
/*
write_reg() caveat:
This doesn't work because D/C has to be LOW for both values:
write_reg(par, val1, val2);
Do it like this:
write_reg(par, val1);
write_reg(par, val2);
*/
/* Init sequence taken from the Adafruit SSD1306 Arduino library */
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
if (par->gamma.curves[0] == 0) {
mutex_lock(&par->gamma.lock);
if (par->info->var.yres == 64)
par->gamma.curves[0] = 0xCF;
else
par->gamma.curves[0] = 0x8F;
mutex_unlock(&par->gamma.lock);
}
/* Set Display OFF */
write_reg(par, 0xAE);
/* Set Display Clock Divide Ratio/ Oscillator Frequency */
write_reg(par, 0xD5);
write_reg(par, 0x80);
/* Set Multiplex Ratio */
write_reg(par, 0xA8);
if (par->info->var.yres == 64)
write_reg(par, 0x3F);
else
write_reg(par, 0x1F);
/* Set Display Offset */
write_reg(par, 0xD3);
write_reg(par, 0x0);
/* Set Display Start Line */
write_reg(par, 0x40 | 0x0);
/* Charge Pump Setting */
write_reg(par, 0x8D);
/* A[2] = 1b, Enable charge pump during display on */
write_reg(par, 0x14);
/* Set Memory Addressing Mode */
write_reg(par, 0x20);
/* Vertical addressing mode */
write_reg(par, 0x01);
/*Set Segment Re-map */
/* column address 127 is mapped to SEG0 */
write_reg(par, 0xA0 | 0x1);
/* Set COM Output Scan Direction */
/* remapped mode. Scan from COM[N-1] to COM0 */
write_reg(par, 0xC8);
/* Set COM Pins Hardware Configuration */
write_reg(par, 0xDA);
if (par->info->var.yres == 64)
/* A[4]=1b, Alternative COM pin configuration */
write_reg(par, 0x12);
else
/* A[4]=0b, Sequential COM pin configuration */
write_reg(par, 0x02);
/* Set Pre-charge Period */
write_reg(par, 0xD9);
write_reg(par, 0xF1);
/* Set VCOMH Deselect Level */
write_reg(par, 0xDB);
/* according to the datasheet, this value is out of bounds */
write_reg(par, 0x40);
/* Entire Display ON */
/* Resume to RAM content display. Output follows RAM content */
write_reg(par, 0xA4);
/* Set Normal Display
0 in RAM: OFF in display panel
1 in RAM: ON in display panel */
write_reg(par, 0xA6);
/* Set Display ON */
write_reg(par, 0xAF);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Set Lower Column Start Address for Page Addressing Mode */
write_reg(par, 0x00 | 0x0);
/* Set Higher Column Start Address for Page Addressing Mode */
write_reg(par, 0x10 | 0x0);
/* Set Display Start Line */
write_reg(par, 0x40 | 0x0);
}
static int blank(struct fbtft_par *par, bool on)
{
fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
__func__, on ? "true" : "false");
if (on)
write_reg(par, 0xAE);
else
write_reg(par, 0xAF);
return 0;
}
/* Gamma is used to control Contrast */
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
curves[0] &= 0xFF;
/* Set Contrast Control for BANK0 */
write_reg(par, 0x81);
write_reg(par, curves[0]);
return 0;
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16 = (u16 *)par->info->screen_base;
u8 *buf = par->txbuf.buf;
int x, y, i;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
for (x = 0; x < par->info->var.xres; x++) {
for (y = 0; y < par->info->var.yres/8; y++) {
*buf = 0x00;
for (i = 0; i < 8; i++)
*buf |= (vmem16[(y*8+i)*par->info->var.xres+x] ? 1 : 0) << i;
buf++;
}
}
/* Write data */
gpio_set_value(par->gpio.dc, 1);
ret = par->fbtftops.write(par, par->txbuf.buf,
par->info->var.xres*par->info->var.yres/8);
if (ret < 0)
dev_err(par->info->device,
"%s: write failed and returned: %d\n", __func__, ret);
return ret;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.gamma_num = 1,
.gamma_len = 1,
.gamma = "00",
.fbtftops = {
.write_vmem = write_vmem,
.init_display = init_display,
.set_addr_win = set_addr_win,
.blank = blank,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1306", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ssd1306");
MODULE_ALIAS("platform:ssd1306");
MODULE_DESCRIPTION("SSD1306 OLED Driver");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,205 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ssd1331"
#define WIDTH 96
#define HEIGHT 64
#define GAMMA_NUM 1
#define GAMMA_LEN 63
#define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2" \
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
write_reg(par, 0xae); /* Display Off */
write_reg(par, 0xa0, 0x70 | (par->bgr << 2)); /* Set Colour Depth */
write_reg(par, 0x72); // RGB colour
write_reg(par, 0xa1, 0x00); /* Set Display Start Line */
write_reg(par, 0xa2, 0x00); /* Set Display Offset */
write_reg(par, 0xa4); /* NORMALDISPLAY */
write_reg(par, 0xa8, 0x3f); // Set multiplex
write_reg(par, 0xad, 0x8e); // Set master
// write_reg(par, 0xb0, 0x0b); // Set power mode
write_reg(par, 0xb1, 0x31); // Precharge
write_reg(par, 0xb3, 0xf0); // Clock div
write_reg(par, 0x8a, 0x64); // Precharge A
write_reg(par, 0x8b, 0x78); // Precharge B
write_reg(par, 0x8c, 0x64); // Precharge C
write_reg(par, 0xbb, 0x3a); // Precharge level
write_reg(par, 0xbe, 0x3e); // vcomh
write_reg(par, 0x87, 0x06); // Master current
write_reg(par, 0x81, 0x91); // Contrast A
write_reg(par, 0x82, 0x50); // Contrast B
write_reg(par, 0x83, 0x7d); // Contrast C
write_reg(par, 0xaf); /* Set Sleep Mode Display On */
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
write_reg(par, 0x15, xs, xe);
write_reg(par, 0x75, ys, ye);
}
static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
va_list args;
int i, ret;
u8 *buf = (u8 *)par->buf;
if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
va_start(args, len);
for (i = 0; i < len; i++) {
buf[i] = (u8)va_arg(args, unsigned int);
}
va_end(args);
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, u8, buf, len, "%s: ", __func__);
}
va_start(args, len);
*buf = (u8)va_arg(args, unsigned int);
if (par->gpio.dc != -1)
gpio_set_value(par->gpio.dc, 0);
ret = par->fbtftops.write(par, par->buf, sizeof(u8));
if (ret < 0) {
va_end(args);
dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret);
return;
}
len--;
if (len) {
i = len;
while (i--) {
*buf++ = (u8)va_arg(args, unsigned int);
}
ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8)));
if (ret < 0) {
va_end(args);
dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret);
return;
}
}
if (par->gpio.dc != -1)
gpio_set_value(par->gpio.dc, 1);
va_end(args);
}
/*
Grayscale Lookup Table
GS1 - GS63
The driver Gamma curve contains the relative values between the entries
in the Lookup table.
From datasheet:
8.8 Gray Scale Decoder
there are total 180 Gamma Settings (Setting 0 to Setting 180)
available for the Gray Scale table.
The gray scale is defined in incremental way, with reference
to the length of previous table entry:
Setting of GS1 has to be >= 0
Setting of GS2 has to be > Setting of GS1 +1
Setting of GS3 has to be > Setting of GS2 +1
:
Setting of GS63 has to be > Setting of GS62 +1
*/
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long tmp[GAMMA_NUM * GAMMA_LEN];
int i, acc = 0;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
for (i = 0; i < 63; i++) {
if (i > 0 && curves[i] < 2) {
dev_err(par->info->device,
"Illegal value in Grayscale Lookup Table at index %d. " \
"Must be greater than 1\n", i);
return -EINVAL;
}
acc += curves[i];
tmp[i] = acc;
if (acc > 180) {
dev_err(par->info->device,
"Illegal value(s) in Grayscale Lookup Table. " \
"At index=%d, the accumulated value has exceeded 180\n", i);
return -EINVAL;
}
}
write_reg(par, 0xB8,
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7],
tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15],
tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23],
tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31],
tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39],
tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47],
tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55],
tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]);
return 0;
}
static int blank(struct fbtft_par *par, bool on)
{
fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
__func__, on ? "true" : "false");
if (on)
write_reg(par, 0xAE);
else
write_reg(par, 0xAF);
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.gamma_num = GAMMA_NUM,
.gamma_len = GAMMA_LEN,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.write_register = write_reg8_bus8,
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_gamma = set_gamma,
.blank = blank,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1331", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ssd1331");
MODULE_ALIAS("platform:ssd1331");
MODULE_DESCRIPTION("SSD1331 OLED Driver");
MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,258 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_ssd1351"
#define WIDTH 128
#define HEIGHT 128
#define GAMMA_NUM 1
#define GAMMA_LEN 63
#define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2 2 " \
"2 2 2 2 2 2 2" \
static void register_onboard_backlight(struct fbtft_par *par);
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
if (par->pdata
&& par->pdata->display.backlight == FBTFT_ONBOARD_BACKLIGHT) {
/* module uses onboard GPIO for panel power */
par->fbtftops.register_backlight = register_onboard_backlight;
}
par->fbtftops.reset(par);
write_reg(par, 0xfd, 0x12); /* Command Lock */
write_reg(par, 0xfd, 0xb1); /* Command Lock */
write_reg(par, 0xae); /* Display Off */
write_reg(par, 0xb3, 0xf1); /* Front Clock Div */
write_reg(par, 0xca, 0x7f); /* Set Mux Ratio */
write_reg(par, 0x15, 0x00, 0x7f); /* Set Column Address */
write_reg(par, 0x75, 0x00, 0x7f); /* Set Row Address */
write_reg(par, 0xa1, 0x00); /* Set Display Start Line */
write_reg(par, 0xa2, 0x00); /* Set Display Offset */
write_reg(par, 0xb5, 0x00); /* Set GPIO */
write_reg(par, 0xab, 0x01); /* Set Function Selection */
write_reg(par, 0xb1, 0x32); /* Set Phase Length */
write_reg(par, 0xb4, 0xa0, 0xb5, 0x55); /* Set Segment Low Voltage */
write_reg(par, 0xbb, 0x17); /* Set Precharge Voltage */
write_reg(par, 0xbe, 0x05); /* Set VComH Voltage */
write_reg(par, 0xc1, 0xc8, 0x80, 0xc8); /* Set Contrast */
write_reg(par, 0xc7, 0x0f); /* Set Master Contrast */
write_reg(par, 0xb6, 0x01); /* Set Second Precharge Period */
write_reg(par, 0xa6); /* Set Display Mode Reset */
write_reg(par, 0xaf); /* Set Sleep Mode Display On */
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
write_reg(par, 0x15, xs, xe);
write_reg(par, 0x75, ys, ye);
write_reg(par, 0x5c);
}
static int set_var(struct fbtft_par *par)
{
unsigned remap;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
if (par->fbtftops.init_display != init_display) {
/* don't risk messing up register A0h */
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par,
"%s: skipping since custom init_display() is used\n",
__func__);
return 0;
}
remap = 0x60 | (par->bgr << 2); /* Set Colour Depth */
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0xA0, remap | 0b00 | 1<<4);
break;
case 270:
write_reg(par, 0xA0, remap | 0b11 | 1<<4);
break;
case 180:
write_reg(par, 0xA0, remap | 0b10);
break;
case 90:
write_reg(par, 0xA0, remap | 0b01);
break;
}
return 0;
}
/*
Grayscale Lookup Table
GS1 - GS63
The driver Gamma curve contains the relative values between the entries
in the Lookup table.
From datasheet:
8.8 Gray Scale Decoder
there are total 180 Gamma Settings (Setting 0 to Setting 180)
available for the Gray Scale table.
The gray scale is defined in incremental way, with reference
to the length of previous table entry:
Setting of GS1 has to be >= 0
Setting of GS2 has to be > Setting of GS1 +1
Setting of GS3 has to be > Setting of GS2 +1
:
Setting of GS63 has to be > Setting of GS62 +1
*/
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
unsigned long tmp[GAMMA_NUM * GAMMA_LEN];
int i, acc = 0;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
for (i = 0; i < 63; i++) {
if (i > 0 && curves[i] < 2) {
dev_err(par->info->device,
"Illegal value in Grayscale Lookup Table at index %d. " \
"Must be greater than 1\n", i);
return -EINVAL;
}
acc += curves[i];
tmp[i] = acc;
if (acc > 180) {
dev_err(par->info->device,
"Illegal value(s) in Grayscale Lookup Table. " \
"At index=%d, the accumulated value has exceeded 180\n", i);
return -EINVAL;
}
}
write_reg(par, 0xB8,
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7],
tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15],
tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23],
tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31],
tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39],
tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47],
tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55],
tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]);
return 0;
}
static int blank(struct fbtft_par *par, bool on)
{
fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n",
__func__, on ? "true" : "false");
if (on)
write_reg(par, 0xAE);
else
write_reg(par, 0xAF);
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.gamma_num = GAMMA_NUM,
.gamma_len = GAMMA_LEN,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
.blank = blank,
},
};
#ifdef CONFIG_FB_BACKLIGHT
static int update_onboard_backlight(struct backlight_device *bd)
{
struct fbtft_par *par = bl_get_data(bd);
bool on;
fbtft_par_dbg(DEBUG_BACKLIGHT, par,
"%s: power=%d, fb_blank=%d\n",
__func__, bd->props.power, bd->props.fb_blank);
on = (bd->props.power == FB_BLANK_UNBLANK)
&& (bd->props.fb_blank == FB_BLANK_UNBLANK);
/* Onboard backlight connected to GPIO0 on SSD1351, GPIO1 unused */
write_reg(par, 0xB5, on ? 0x03 : 0x02);
return 0;
}
static void register_onboard_backlight(struct fbtft_par *par)
{
struct backlight_device *bd;
struct backlight_properties bl_props = { 0, };
struct backlight_ops *bl_ops;
fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__);
bl_ops = devm_kzalloc(par->info->device, sizeof(struct backlight_ops),
GFP_KERNEL);
if (!bl_ops) {
dev_err(par->info->device,
"%s: could not allocate memory for backlight operations.\n",
__func__);
return;
}
bl_ops->update_status = update_onboard_backlight;
bl_props.type = BACKLIGHT_RAW;
bl_props.power = FB_BLANK_POWERDOWN;
bd = backlight_device_register(dev_driver_string(par->info->device),
par->info->device, par, bl_ops, &bl_props);
if (IS_ERR(bd)) {
dev_err(par->info->device,
"cannot register backlight device (%ld)\n",
PTR_ERR(bd));
return;
}
par->info->bl_dev = bd;
if (!par->fbtftops.unregister_backlight)
par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
}
#else
static void register_onboard_backlight(struct fbtft_par *par) { };
#endif
FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1351", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:ssd1351");
MODULE_ALIAS("platform:ssd1351");
MODULE_DESCRIPTION("SSD1351 OLED Driver");
MODULE_AUTHOR("James Davies");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,195 @@
/*
* FB driver for the ST7735R LCD Controller
*
* 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/init.h>
#include "fbtft.h"
#define DRVNAME "fb_st7735r"
#define DEFAULT_GAMMA "0F 1A 0F 18 2F 28 20 22 1F 1B 23 37 00 07 02 10\n" \
"0F 1B 0F 17 33 2C 29 2E 30 30 39 3F 00 07 03 10"
static int default_init_sequence[] = {
/* SWRESET - Software reset */
-1, 0x01,
-2, 150, /* delay */
/* SLPOUT - Sleep out & booster on */
-1, 0x11,
-2, 500, /* delay */
/* FRMCTR1 - frame rate control: normal mode
frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D) */
-1, 0xB1, 0x01, 0x2C, 0x2D,
/* FRMCTR2 - frame rate control: idle mode
frame rate = fosc / (1 x 2 + 40) * (LINE + 2C + 2D) */
-1, 0xB2, 0x01, 0x2C, 0x2D,
/* FRMCTR3 - frame rate control - partial mode
dot inversion mode, line inversion mode */
-1, 0xB3, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D,
/* INVCTR - display inversion control
no inversion */
-1, 0xB4, 0x07,
/* PWCTR1 - Power Control
-4.6V, AUTO mode */
-1, 0xC0, 0xA2, 0x02, 0x84,
/* PWCTR2 - Power Control
VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD */
-1, 0xC1, 0xC5,
/* PWCTR3 - Power Control
Opamp current small, Boost frequency */
-1, 0xC2, 0x0A, 0x00,
/* PWCTR4 - Power Control
BCLK/2, Opamp current small & Medium low */
-1, 0xC3,0x8A,0x2A,
/* PWCTR5 - Power Control */
-1, 0xC4, 0x8A, 0xEE,
/* VMCTR1 - Power Control */
-1, 0xC5, 0x0E,
/* INVOFF - Display inversion off */
-1, 0x20,
/* COLMOD - Interface pixel format */
-1, 0x3A, 0x05,
/* DISPON - Display On */
-1, 0x29,
-2, 100, /* delay */
/* NORON - Partial off (Normal) */
-1, 0x13,
-2, 10, /* delay */
/* end marker */
-3
};
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address */
write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
/* Row adress */
write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
/* Memory write */
write_reg(par, 0x2C);
}
#define MY (1 << 7)
#define MX (1 << 6)
#define MV (1 << 5)
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* MADCTL - Memory data access control
RGB/BGR:
1. Mode selection pin SRGB
RGB H/W pin for color filter setting: 0=RGB, 1=BGR
2. MADCTL RGB bit
RGB-BGR ORDER color filter panel: 0=RGB, 1=BGR */
switch (par->info->var.rotate) {
case 0:
write_reg(par, 0x36, MX | MY | (par->bgr << 3));
break;
case 270:
write_reg(par, 0x36, MY | MV | (par->bgr << 3));
break;
case 180:
write_reg(par, 0x36, (par->bgr << 3));
break;
case 90:
write_reg(par, 0x36, MX | MV | (par->bgr << 3));
break;
}
return 0;
}
/*
Gamma string format:
VRF0P VOS0P PK0P PK1P PK2P PK3P PK4P PK5P PK6P PK7P PK8P PK9P SELV0P SELV1P SELV62P SELV63P
VRF0N VOS0N PK0N PK1N PK2N PK3N PK4N PK5N PK6N PK7N PK8N PK9N SELV0N SELV1N SELV62N SELV63N
*/
#define CURVE(num, idx) curves[num*par->gamma.num_values + idx]
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
int i,j;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
for (i = 0; i < par->gamma.num_curves; i++)
for (j = 0; j < par->gamma.num_values; j++)
CURVE(i,j) &= 0b111111;
for (i = 0; i < par->gamma.num_curves; i++)
write_reg(par, 0xE0 + i,
CURVE(i, 0), CURVE(i, 1), CURVE(i, 2), CURVE(i, 3),
CURVE(i, 4), CURVE(i, 5), CURVE(i, 6), CURVE(i, 7),
CURVE(i, 8), CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
CURVE(i, 12), CURVE(i, 13), CURVE(i, 14), CURVE(i,15));
return 0;
}
#undef CURVE
static struct fbtft_display display = {
.regwidth = 8,
.width = 128,
.height = 160,
.init_sequence = default_init_sequence,
.gamma_num = 2,
.gamma_len = 16,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.set_addr_win = set_addr_win,
.set_var = set_var,
.set_gamma = set_gamma,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7735r", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:st7735r");
MODULE_ALIAS("platform:st7735r");
MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,124 @@
/*
* Custom FB driver for tinylcd.com display
*
* 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/init.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_tinylcd"
#define WIDTH 320
#define HEIGHT 480
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
write_reg(par, 0xB0, 0x80);
write_reg(par, 0xC0, 0x0A, 0x0A);
write_reg(par, 0xC1, 0x45, 0x07);
write_reg(par, 0xC2, 0x33);
write_reg(par, 0xC5, 0x00, 0x42, 0x80);
write_reg(par, 0xB1, 0xD0, 0x11);
write_reg(par, 0xB4, 0x02);
write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
write_reg(par, 0xB7, 0x07);
write_reg(par, 0x36, 0x58);
write_reg(par, 0xF0, 0x36, 0xA5, 0xD3);
write_reg(par, 0xE5, 0x80);
write_reg(par, 0xE5, 0x01);
write_reg(par, 0xB3, 0x00);
write_reg(par, 0xE5, 0x00);
write_reg(par, 0xF0, 0x36, 0xA5, 0x53);
write_reg(par, 0xE0, 0x00, 0x35, 0x33, 0x00, 0x00, 0x00,
0x00, 0x35, 0x33, 0x00, 0x00, 0x00);
write_reg(par, 0x3A, 0x55);
write_reg(par, 0x11);
udelay(250);
write_reg(par, 0x29);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address */
write_reg(par, 0x2A, xs >> 8, xs & 0xFF, xe >> 8, xe & 0xFF);
/* Row adress */
write_reg(par, 0x2B, ys >> 8, ys & 0xFF, ye >> 8, ye & 0xFF);
/* Memory write */
write_reg(par, 0x2C);
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
case 270:
write_reg(par, 0xB6, 0x00, 0x02, 0x3B);
write_reg(par, 0x36, 0x28);
break;
case 180:
write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
write_reg(par, 0x36, 0x58);
break;
case 90:
write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
write_reg(par, 0x36, 0x38);
break;
default:
write_reg(par, 0xB6, 0x00, 0x22, 0x3B);
write_reg(par, 0x36, 0x08);
break;
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "neosec,tinylcd", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("spi:tinylcd");
MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,176 @@
/*
* FB driver for the TLS8204 LCD Controller
*
* The display is monochrome and the video memory is RGB565.
* Any pixel value except 0 turns the pixel on.
*
* Copyright (C) 2013 Noralf Tronnes
* Copyright (C) 2014 Michael Hope (adapted for the TLS8204)
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_tls8204"
#define WIDTH 84
#define HEIGHT 48
#define TXBUFLEN WIDTH
#define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */
static unsigned bs = 4;
module_param(bs, uint, 0);
MODULE_PARM_DESC(bs, "BS[2:0] Bias voltage level: 0-7 (default: 4)");
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
/* Enter extended command mode */
write_reg(par, 0x21); /* 5:1 1
2:0 PD - Powerdown control: chip is active
1:0 V - Entry mode: horizontal addressing
0:1 H - Extended instruction set control: extended
*/
/* H=1 Bias system */
write_reg(par, 0x10 | (bs & 0x7)); /*
4:1 1
3:0 0
2:x BS2 - Bias System
1:x BS1
0:x BS0
*/
/* Set the address of the first display line. */
write_reg(par, 0x04 | (64 >> 6));
write_reg(par, 0x40 | (64 & 0x3F));
/* Enter H=0 standard command mode */
write_reg(par, 0x20);
/* H=0 Display control */
write_reg(par, 0x08 | 4); /*
3:1 1
2:1 D - DE: 10=normal mode
1:0 0
0:0 E
*/
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* H=0 Set X address of RAM */
write_reg(par, 0x80); /* 7:1 1
6-0: X[6:0] - 0x00
*/
/* H=0 Set Y address of RAM */
write_reg(par, 0x40); /* 7:0 0
6:1 1
2-0: Y[2:0] - 0x0
*/
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16 = (u16 *)par->info->screen_base;
int x, y, i;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
for (y = 0; y < HEIGHT/8; y++) {
u8 *buf = par->txbuf.buf;
/* The display is 102x68 but the LCD is 84x48. Set
the write pointer at the start of each row. */
gpio_set_value(par->gpio.dc, 0);
write_reg(par, 0x80 | 0);
write_reg(par, 0x40 | y);
for (x = 0; x < WIDTH; x++) {
u8 ch = 0;
for (i = 0; i < 8*WIDTH; i += WIDTH) {
ch >>= 1;
if (vmem16[(y*8*WIDTH)+i+x])
ch |= 0x80;
}
*buf++ = ch;
}
/* Write the row */
gpio_set_value(par->gpio.dc, 1);
ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
if (ret < 0) {
dev_err(par->info->device,
"%s: write failed and returned: %d\n", __func__, ret);
break;
}
}
return ret;
}
static int set_gamma(struct fbtft_par *par, unsigned long *curves)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* apply mask */
curves[0] &= 0x7F;
write_reg(par, 0x21); /* turn on extended instruction set */
write_reg(par, 0x80 | curves[0]);
write_reg(par, 0x20); /* turn off extended instruction set */
return 0;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.txbuflen = TXBUFLEN,
.gamma_num = 1,
.gamma_len = 1,
.gamma = DEFAULT_GAMMA,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.write_vmem = write_vmem,
.set_gamma = set_gamma,
},
.backlight = 1,
};
FBTFT_REGISTER_DRIVER(DRVNAME, "teralane,tls8204", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("spi:tls8204");
MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller");
MODULE_AUTHOR("Michael Hope");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,210 @@
/*
* FB driver for the UC1701 LCD Controller
*
* The display is monochrome and the video memory is RGB565.
* Any pixel value except 0 turns the pixel on.
*
* Copyright (C) 2014 Juergen Holzmann
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_uc1701"
#define WIDTH 102
#define HEIGHT 64
#define PAGES (HEIGHT/8)
/* 1: Display on/off */
#define LCD_DISPLAY_ENABLE 0xAE
/* 2: display start line set */
#define LCD_START_LINE 0x40
/* 3: Page address set (lower 4 bits select one of the pages) */
#define LCD_PAGE_ADDRESS 0xB0
/* 4: column address */
#define LCD_COL_ADDRESS 0x10
/* 8: select orientation */
#define LCD_BOTTOMVIEW 0xA0
/* 9: inverted display */
#define LCD_DISPLAY_INVERT 0xA6
/* 10: show memory content or switch all pixels on */
#define LCD_ALL_PIXEL 0xA4
/* 11: lcd bias set */
#define LCD_BIAS 0xA2
/* 14: Reset Controller */
#define LCD_RESET_CMD 0xE2
/* 15: output mode select (turns display upside-down) */
#define LCD_SCAN_DIR 0xC0
/* 16: power control set */
#define LCD_POWER_CONTROL 0x28
/* 17: voltage regulator resistor ratio set */
#define LCD_VOLTAGE 0x20
/* 18: Volume mode set */
#define LCD_VOLUME_MODE 0x81
/* 22: NOP command */
#define LCD_NO_OP 0xE3
/* 25: advanced program control */
#define LCD_ADV_PROG_CTRL 0xFA
/* 25: advanced program control2 */
#define LCD_ADV_PROG_CTRL2 0x10
#define LCD_TEMPCOMP_HIGH 0x80
/* column offset for normal orientation */
#define SHIFT_ADDR_NORMAL 0
/* column offset for bottom view orientation */
#define SHIFT_ADDR_TOPVIEW 30
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
/* softreset of LCD */
write_reg(par, LCD_RESET_CMD);
mdelay(10);
/* set startpoint */
/* LCD_START_LINE | (pos & 0x3F) */
write_reg(par, LCD_START_LINE);
/* select orientation BOTTOMVIEW */
write_reg(par, LCD_BOTTOMVIEW | 1);
/* output mode select (turns display upside-down) */
write_reg(par, LCD_SCAN_DIR | 0x00);
/* Normal Pixel mode */
write_reg(par, LCD_ALL_PIXEL | 0);
/* positive display */
write_reg(par, LCD_DISPLAY_INVERT | 0);
/* bias 1/9 */
write_reg(par, LCD_BIAS | 0);
/* power control mode: all features on */
/* LCD_POWER_CONTROL | (val&0x07) */
write_reg(par, LCD_POWER_CONTROL | 0x07);
/* set voltage regulator R/R */
/* LCD_VOLTAGE | (val&0x07) */
write_reg(par, LCD_VOLTAGE | 0x07);
/* volume mode set */
/* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
write_reg(par, LCD_VOLUME_MODE);
/* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
write_reg(par, 0x09);
/* ???? */
/* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */
write_reg(par, LCD_NO_OP);
/* advanced program control */
write_reg(par, LCD_ADV_PROG_CTRL);
write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH);
/* enable display */
write_reg(par, LCD_DISPLAY_ENABLE | 1);
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* goto address */
/* LCD_PAGE_ADDRESS | ((page) & 0x1F),
(((col)+SHIFT_ADDR_NORMAL) & 0x0F),
LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
write_reg(par, LCD_PAGE_ADDRESS);
/* LCD_PAGE_ADDRESS | ((page) & 0x1F),
(((col)+SHIFT_ADDR_NORMAL) & 0x0F),
LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
write_reg(par, 0x00);
/* LCD_PAGE_ADDRESS | ((page) & 0x1F),
(((col)+SHIFT_ADDR_NORMAL) & 0x0F),
LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
write_reg(par, LCD_COL_ADDRESS);
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16 = (u16 *)par->info->screen_base;
u8 *buf = par->txbuf.buf;
int x, y, i;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
for (y = 0; y < PAGES; y++) {
buf = par->txbuf.buf;
for (x = 0; x < WIDTH; x++) {
*buf = 0x00;
for (i = 0; i < 8; i++)
*buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i;
buf++;
}
/* LCD_PAGE_ADDRESS | ((page) & 0x1F),
(((col)+SHIFT_ADDR_NORMAL) & 0x0F),
LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
write_reg(par, LCD_PAGE_ADDRESS|(u8)y);
/* LCD_PAGE_ADDRESS | ((page) & 0x1F),
(((col)+SHIFT_ADDR_NORMAL) & 0x0F),
LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
write_reg(par, 0x00);
/* LCD_PAGE_ADDRESS | ((page) & 0x1F),
(((col)+SHIFT_ADDR_NORMAL) & 0x0F),
LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */
write_reg(par, LCD_COL_ADDRESS);
gpio_set_value(par->gpio.dc, 1);
ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH);
gpio_set_value(par->gpio.dc, 0);
}
if (ret < 0)
dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret);
return ret;
}
static struct fbtft_display display = {
.regwidth = 8,
.width = WIDTH,
.height = HEIGHT,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.write_vmem = write_vmem,
},
.backlight = 1,
};
FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("spi:uc1701");
MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller");
MODULE_AUTHOR("Juergen Holzmann");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,206 @@
/*
* FB driver for the uPD161704 LCD Controller
*
* Copyright (C) 2014 Seong-Woo Kim
*
* Based on fb_ili9325.c by Noralf Tronnes
* Based on ili9325.c by Jeroen Domburg
* Init code from UTFT library by Henning Karlsen
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_upd161704"
#define WIDTH 240
#define HEIGHT 320
#define BPP 16
static int init_display(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
par->fbtftops.reset(par);
if (par->gpio.cs != -1)
gpio_set_value(par->gpio.cs, 0); /* Activate chip */
/* Initialization sequence from Lib_UTFT */
/* register reset */
write_reg(par, 0x0003,0x0001); /* Soft reset */
/* oscillator start */
write_reg(par, 0x003A,0x0001); /*Oscillator 0: stop, 1: operation */
udelay(100);
/* y-setting */
write_reg(par, 0x0024,0x007B); /* amplitude setting */
udelay(10);
write_reg(par, 0x0025,0x003B); /* amplitude setting */
write_reg(par, 0x0026,0x0034); /* amplitude setting */
udelay(10);
write_reg(par, 0x0027,0x0004); /* amplitude setting */
write_reg(par, 0x0052,0x0025); /* circuit setting 1 */
udelay(10);
write_reg(par, 0x0053,0x0033); /* circuit setting 2 */
write_reg(par, 0x0061,0x001C); /* adjustment V10 positive polarity */
udelay(10);
write_reg(par, 0x0062,0x002C); /* adjustment V9 negative polarity */
write_reg(par, 0x0063,0x0022); /* adjustment V34 positive polarity */
udelay(10);
write_reg(par, 0x0064,0x0027); /* adjustment V31 negative polarity */
udelay(10);
write_reg(par, 0x0065,0x0014); /* adjustment V61 negative polarity */
udelay(10);
write_reg(par, 0x0066,0x0010); /* adjustment V61 negative polarity */
/* Basical clock for 1 line (BASECOUNT[7:0]) number specified */
write_reg(par, 0x002E,0x002D);
/* Power supply setting */
write_reg(par, 0x0019,0x0000); /* DC/DC output setting */
udelay(200);
write_reg(par, 0x001A,0x1000); /* DC/DC frequency setting */
write_reg(par, 0x001B,0x0023); /* DC/DC rising setting */
write_reg(par, 0x001C,0x0C01); /* Regulator voltage setting */
write_reg(par, 0x001D,0x0000); /* Regulator current setting */
write_reg(par, 0x001E,0x0009); /* VCOM output setting */
write_reg(par, 0x001F,0x0035); /* VCOM amplitude setting */
write_reg(par, 0x0020,0x0015); /* VCOMM cencter setting */
write_reg(par, 0x0018,0x1E7B); /* DC/DC operation setting */
/* windows setting */
write_reg(par, 0x0008,0x0000); /* Minimum X address */
write_reg(par, 0x0009,0x00EF); /* Maximum X address */
write_reg(par, 0x000a,0x0000); /* Minimum Y address */
write_reg(par, 0x000b,0x013F); /* Maximum Y address */
/* LCD display area setting */
write_reg(par, 0x0029,0x0000); /* [LCDSIZE] X MIN. size set */
write_reg(par, 0x002A,0x0000); /* [LCDSIZE] Y MIN. size set */
write_reg(par, 0x002B,0x00EF); /* [LCDSIZE] X MAX. size set */
write_reg(par, 0x002C,0x013F); /* [LCDSIZE] Y MAX. size set */
/* Gate scan setting */
write_reg(par, 0x0032,0x0002);
/* n line inversion line number */
write_reg(par, 0x0033,0x0000);
/* Line inversion/frame inversion/interlace setting */
write_reg(par, 0x0037,0x0000);
/* Gate scan operation setting register */
write_reg(par, 0x003B,0x0001);
/* Color mode */
/*GS = 0: 260-k color (64 gray scale), GS = 1: 8 color (2 gray scale) */
write_reg(par, 0x0004,0x0000);
/* RAM control register */
write_reg(par, 0x0005,0x0000); /*Window access 00:Normal, 10:Window */
/* Display setting register 2 */
write_reg(par, 0x0001,0x0000);
/* display setting */
write_reg(par, 0x0000,0x0000); /* display on */
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par,
"%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R20h = Horizontal GRAM Start Address */
/* R21h = Vertical GRAM Start Address */
case 0:
write_reg(par, 0x0006, xs);
write_reg(par, 0x0007, ys);
break;
case 180:
write_reg(par, 0x0006, WIDTH - 1 - xs);
write_reg(par, 0x0007, HEIGHT - 1 - ys);
break;
case 270:
write_reg(par, 0x0006, WIDTH - 1 - ys);
write_reg(par, 0x0007, xs);
break;
case 90:
write_reg(par, 0x0006, ys);
write_reg(par, 0x0007, HEIGHT - 1 - xs);
break;
}
write_reg(par, 0x0e); /* Write Data to GRAM */
}
static int set_var(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
switch (par->info->var.rotate) {
/* AM: GRAM update direction */
case 0:
write_reg(par, 0x01, 0x0000);
write_reg(par, 0x05, 0x0000);
break;
case 180:
write_reg(par, 0x01, 0x00C0);
write_reg(par, 0x05, 0x0000);
break;
case 270:
write_reg(par, 0x01, 0x0080);
write_reg(par, 0x05, 0x0001);
break;
case 90:
write_reg(par, 0x01, 0x0040);
write_reg(par, 0x05, 0x0001);
break;
}
return 0;
}
static struct fbtft_display display = {
.regwidth = 16,
.width = WIDTH,
.height = HEIGHT,
.fbtftops = {
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "nec,upd161704", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_ALIAS("platform:" DRVNAME);
MODULE_ALIAS("spi:upd161704");
MODULE_ALIAS("platform:upd161704");
MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller");
MODULE_AUTHOR("Seong-Woo Kim");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,324 @@
/*
* FB driver for the Watterott LCD Controller
*
* 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/init.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "fb_watterott"
#define WIDTH 320
#define HEIGHT 240
#define FPS 5
#define TXBUFLEN 1024
#define DEFAULT_BRIGHTNESS 50
#define CMD_VERSION 0x01
#define CMD_LCD_LED 0x10
#define CMD_LCD_RESET 0x11
#define CMD_LCD_ORIENTATION 0x20
#define CMD_LCD_DRAWIMAGE 0x27
#define COLOR_RGB323 8
#define COLOR_RGB332 9
#define COLOR_RGB233 10
#define COLOR_RGB565 16
static short mode = 565;
module_param(mode, short, 0);
MODULE_PARM_DESC(mode, "RGB color transfer mode: 332, 565 (default)");
static void write_reg8_bus8(struct fbtft_par *par, int len, ...)
{
va_list args;
int i, ret;
u8 *buf = par->buf;
va_start(args, len);
for (i = 0; i < len; i++)
*buf++ = (u8)va_arg(args, unsigned int);
va_end(args);
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
par->info->device, u8, par->buf, len, "%s: ", __func__);
ret = par->fbtftops.write(par, par->buf, len);
if (ret < 0) {
dev_err(par->info->device,
"%s: write() failed and returned %d\n", __func__, ret);
return;
}
}
static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
{
unsigned start_line, end_line;
u16 *vmem16 = (u16 *)(par->info->screen_base + offset);
u16 *pos = par->txbuf.buf + 1;
u16 *buf16 = par->txbuf.buf + 10;
int i, j;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
start_line = offset / par->info->fix.line_length;
end_line = start_line + (len / par->info->fix.line_length) - 1;
/* Set command header. pos: x, y, w, h */
((u8 *)par->txbuf.buf)[0] = CMD_LCD_DRAWIMAGE;
pos[0] = 0;
pos[2] = cpu_to_be16(par->info->var.xres);
pos[3] = cpu_to_be16(1);
((u8 *)par->txbuf.buf)[9] = COLOR_RGB565;
for (i = start_line; i <= end_line; i++) {
pos[1] = cpu_to_be16(i);
for (j = 0; j < par->info->var.xres; j++)
buf16[j] = cpu_to_be16(*vmem16++);
ret = par->fbtftops.write(par,
par->txbuf.buf, 10 + par->info->fix.line_length);
if (ret < 0)
return ret;
udelay(300);
}
return 0;
}
#define RGB565toRGB323(c) (((c&0xE000)>>8) | ((c&0600)>>6) | ((c&0x001C)>>2))
#define RGB565toRGB332(c) (((c&0xE000)>>8) | ((c&0700)>>6) | ((c&0x0018)>>3))
#define RGB565toRGB233(c) (((c&0xC000)>>8) | ((c&0700)>>5) | ((c&0x001C)>>2))
static int write_vmem_8bit(struct fbtft_par *par, size_t offset, size_t len)
{
unsigned start_line, end_line;
u16 *vmem16 = (u16 *)(par->info->screen_base + offset);
u16 *pos = par->txbuf.buf + 1;
u8 *buf8 = par->txbuf.buf + 10;
int i, j;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__);
start_line = offset / par->info->fix.line_length;
end_line = start_line + (len / par->info->fix.line_length) - 1;
/* Set command header. pos: x, y, w, h */
((u8 *)par->txbuf.buf)[0] = CMD_LCD_DRAWIMAGE;
pos[0] = 0;
pos[2] = cpu_to_be16(par->info->var.xres);
pos[3] = cpu_to_be16(1);
((u8 *)par->txbuf.buf)[9] = COLOR_RGB332;
for (i = start_line; i <= end_line; i++) {
pos[1] = cpu_to_be16(i);
for (j = 0; j < par->info->var.xres; j++) {
buf8[j] = RGB565toRGB332(*vmem16);
vmem16++;
}
ret = par->fbtftops.write(par,
par->txbuf.buf, 10 + par->info->var.xres);
if (ret < 0)
return ret;
udelay(700);
}
return 0;
}
static unsigned firmware_version(struct fbtft_par *par)
{
u8 rxbuf[4] = {0, };
write_reg(par, CMD_VERSION);
par->fbtftops.read(par, rxbuf, 4);
if (rxbuf[1] != '.')
return 0;
return (rxbuf[0] - '0') << 8 | (rxbuf[2] - '0') << 4 | (rxbuf[3] - '0');
}
static int init_display(struct fbtft_par *par)
{
int ret;
unsigned version;
u8 save_mode;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* enable SPI interface by having CS and MOSI low during reset */
save_mode = par->spi->mode;
par->spi->mode |= SPI_CS_HIGH;
ret = par->spi->master->setup(par->spi); /* set CS inactive low */
if (ret) {
dev_err(par->info->device, "Could not set SPI_CS_HIGH\n");
return ret;
}
write_reg(par, 0x00); /* make sure mode is set */
mdelay(50);
par->fbtftops.reset(par);
mdelay(1000);
par->spi->mode = save_mode;
ret = par->spi->master->setup(par->spi);
if (ret) {
dev_err(par->info->device, "Could not restore SPI mode\n");
return ret;
}
write_reg(par, 0x00);
version = firmware_version(par);
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Firmware version: %x.%02x\n",
version >> 8, version & 0xFF);
if (mode == 332)
par->fbtftops.write_vmem = write_vmem_8bit;
return 0;
}
static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
/* not used on this controller */
}
static int set_var(struct fbtft_par *par)
{
u8 rotate;
fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__);
/* this controller rotates clock wise */
switch (par->info->var.rotate) {
case 90:
rotate = 27;
break;
case 180:
rotate = 18;
break;
case 270:
rotate = 9;
break;
default:
rotate = 0;
}
write_reg(par, CMD_LCD_ORIENTATION, rotate);
return 0;
}
static int verify_gpios(struct fbtft_par *par)
{
if (par->gpio.reset < 0) {
dev_err(par->info->device, "Missing 'reset' gpio. Aborting.\n");
return -EINVAL;
}
return 0;
}
#ifdef CONFIG_FB_BACKLIGHT
static int backlight_chip_update_status(struct backlight_device *bd)
{
struct fbtft_par *par = bl_get_data(bd);
int brightness = bd->props.brightness;
fbtft_par_dbg(DEBUG_BACKLIGHT, par,
"%s: brightness=%d, power=%d, fb_blank=%d\n",
__func__, bd->props.brightness, bd->props.power,
bd->props.fb_blank);
if (bd->props.power != FB_BLANK_UNBLANK)
brightness = 0;
if (bd->props.fb_blank != FB_BLANK_UNBLANK)
brightness = 0;
write_reg(par, CMD_LCD_LED, brightness);
return 0;
}
static void register_chip_backlight(struct fbtft_par *par)
{
struct backlight_device *bd;
struct backlight_properties bl_props = { 0, };
struct backlight_ops *bl_ops;
fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__);
bl_ops = devm_kzalloc(par->info->device, sizeof(struct backlight_ops),
GFP_KERNEL);
if (!bl_ops) {
dev_err(par->info->device,
"%s: could not allocate memory for backlight operations.\n",
__func__);
return;
}
bl_ops->update_status = backlight_chip_update_status;
bl_props.type = BACKLIGHT_RAW;
bl_props.power = FB_BLANK_POWERDOWN;
bl_props.max_brightness = 100;
bl_props.brightness = DEFAULT_BRIGHTNESS;
bd = backlight_device_register(dev_driver_string(par->info->device),
par->info->device, par, bl_ops, &bl_props);
if (IS_ERR(bd)) {
dev_err(par->info->device,
"cannot register backlight device (%ld)\n",
PTR_ERR(bd));
return;
}
par->info->bl_dev = bd;
if (!par->fbtftops.unregister_backlight)
par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
}
#else
#define register_chip_backlight NULL
#endif
static struct fbtft_display display = {
.regwidth = 8,
.buswidth = 8,
.width = WIDTH,
.height = HEIGHT,
.fps = FPS,
.txbuflen = TXBUFLEN,
.fbtftops = {
.write_register = write_reg8_bus8,
.write_vmem = write_vmem,
.init_display = init_display,
.set_addr_win = set_addr_win,
.set_var = set_var,
.verify_gpios = verify_gpios,
.register_backlight = register_chip_backlight,
},
};
FBTFT_REGISTER_DRIVER(DRVNAME, "watterott,openlcd", &display);
MODULE_ALIAS("spi:" DRVNAME);
MODULE_DESCRIPTION("FB driver for the Watterott LCD Controller");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,256 @@
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include "fbtft.h"
/*****************************************************************************
*
* void (*write_reg)(struct fbtft_par *par, int len, ...);
*
*****************************************************************************/
#define define_fbtft_write_reg(func, type, modifier) \
void func(struct fbtft_par *par, int len, ...) \
{ \
va_list args; \
int i, ret; \
int offset = 0; \
type *buf = (type *)par->buf; \
\
if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { \
va_start(args, len); \
for (i = 0; i < len; i++) { \
buf[i] = (type)va_arg(args, unsigned int); \
} \
va_end(args); \
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, par->info->device, type, buf, len, "%s: ", __func__); \
} \
\
va_start(args, len); \
\
if (par->startbyte) { \
*(u8 *)par->buf = par->startbyte; \
buf = (type *)(par->buf + 1); \
offset = 1; \
} \
\
*buf = modifier((type)va_arg(args, unsigned int)); \
if (par->gpio.dc != -1) \
gpio_set_value(par->gpio.dc, 0); \
ret = par->fbtftops.write(par, par->buf, sizeof(type)+offset); \
if (ret < 0) { \
va_end(args); \
dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \
return; \
} \
len--; \
\
if (par->startbyte) \
*(u8 *)par->buf = par->startbyte | 0x2; \
\
if (len) { \
i = len; \
while (i--) { \
*buf++ = modifier((type)va_arg(args, unsigned int)); \
} \
if (par->gpio.dc != -1) \
gpio_set_value(par->gpio.dc, 1); \
ret = par->fbtftops.write(par, par->buf, len * (sizeof(type)+offset)); \
if (ret < 0) { \
va_end(args); \
dev_err(par->info->device, "%s: write() failed and returned %d\n", __func__, ret); \
return; \
} \
} \
va_end(args); \
} \
EXPORT_SYMBOL(func);
define_fbtft_write_reg(fbtft_write_reg8_bus8, u8, )
define_fbtft_write_reg(fbtft_write_reg16_bus8, u16, cpu_to_be16)
define_fbtft_write_reg(fbtft_write_reg16_bus16, u16, )
void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...)
{
va_list args;
int i, ret;
int pad = 0;
u16 *buf = (u16 *)par->buf;
if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) {
va_start(args, len);
for (i = 0; i < len; i++)
*(((u8 *)buf) + i) = (u8)va_arg(args, unsigned int);
va_end(args);
fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par,
par->info->device, u8, buf, len, "%s: ", __func__);
}
if (len <= 0)
return;
if (par->spi && (par->spi->bits_per_word == 8)) {
/* we're emulating 9-bit, pad start of buffer with no-ops
(assuming here that zero is a no-op) */
pad = (len % 4) ? 4 - (len % 4) : 0;
for (i = 0; i < pad; i++)
*buf++ = 0x000;
}
va_start(args, len);
*buf++ = (u8)va_arg(args, unsigned int);
i = len - 1;
while (i--) {
*buf = (u8)va_arg(args, unsigned int);
*buf++ |= 0x100; /* dc=1 */
}
va_end(args);
ret = par->fbtftops.write(par, par->buf, (len + pad) * sizeof(u16));
if (ret < 0) {
dev_err(par->info->device,
"%s: write() failed and returned %d\n", __func__, ret);
return;
}
}
EXPORT_SYMBOL(fbtft_write_reg8_bus9);
/*****************************************************************************
*
* int (*write_vmem)(struct fbtft_par *par);
*
*****************************************************************************/
/* 16 bit pixel over 8-bit databus */
int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16;
u16 *txbuf16 = (u16 *)par->txbuf.buf;
size_t remain;
size_t to_copy;
size_t tx_array_size;
int i;
int ret = 0;
size_t startbyte_size = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
__func__, offset, len);
remain = len / 2;
vmem16 = (u16 *)(par->info->screen_base + offset);
if (par->gpio.dc != -1)
gpio_set_value(par->gpio.dc, 1);
/* non buffered write */
if (!par->txbuf.buf)
return par->fbtftops.write(par, vmem16, len);
/* buffered write */
tx_array_size = par->txbuf.len / 2;
if (par->startbyte) {
txbuf16 = (u16 *)(par->txbuf.buf + 1);
tx_array_size -= 2;
*(u8 *)(par->txbuf.buf) = par->startbyte | 0x2;
startbyte_size = 1;
}
while (remain) {
to_copy = remain > tx_array_size ? tx_array_size : remain;
dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n",
to_copy, remain - to_copy);
for (i = 0; i < to_copy; i++)
txbuf16[i] = cpu_to_be16(vmem16[i]);
vmem16 = vmem16 + to_copy;
ret = par->fbtftops.write(par, par->txbuf.buf,
startbyte_size + to_copy * 2);
if (ret < 0)
return ret;
remain -= to_copy;
}
return ret;
}
EXPORT_SYMBOL(fbtft_write_vmem16_bus8);
/* 16 bit pixel over 9-bit SPI bus: dc + high byte, dc + low byte */
int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len)
{
u8 *vmem8;
u16 *txbuf16 = par->txbuf.buf;
size_t remain;
size_t to_copy;
size_t tx_array_size;
int i;
int ret = 0;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
__func__, offset, len);
if (!par->txbuf.buf) {
dev_err(par->info->device, "%s: txbuf.buf is NULL\n", __func__);
return -1;
}
remain = len;
vmem8 = par->info->screen_base + offset;
tx_array_size = par->txbuf.len / 2;
while (remain) {
to_copy = remain > tx_array_size ? tx_array_size : remain;
dev_dbg(par->info->device, " to_copy=%zu, remain=%zu\n",
to_copy, remain - to_copy);
#ifdef __LITTLE_ENDIAN
for (i = 0; i < to_copy; i += 2) {
txbuf16[i] = 0x0100 | vmem8[i+1];
txbuf16[i+1] = 0x0100 | vmem8[i];
}
#else
for (i = 0; i < to_copy; i++)
txbuf16[i] = 0x0100 | vmem8[i];
#endif
vmem8 = vmem8 + to_copy;
ret = par->fbtftops.write(par, par->txbuf.buf, to_copy*2);
if (ret < 0)
return ret;
remain -= to_copy;
}
return ret;
}
EXPORT_SYMBOL(fbtft_write_vmem16_bus9);
int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len)
{
dev_err(par->info->device, "%s: function not implemented\n", __func__);
return -1;
}
EXPORT_SYMBOL(fbtft_write_vmem8_bus8);
/* 16 bit pixel over 16-bit databus */
int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len)
{
u16 *vmem16;
fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s(offset=%zu, len=%zu)\n",
__func__, offset, len);
vmem16 = (u16 *)(par->info->screen_base + offset);
if (par->gpio.dc != -1)
gpio_set_value(par->gpio.dc, 1);
/* no need for buffered write with 16-bit bus */
return par->fbtftops.write(par, vmem16, len);
}
EXPORT_SYMBOL(fbtft_write_vmem16_bus16);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,409 @@
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#ifdef CONFIG_ARCH_BCM2708
#include <mach/platform.h>
#endif
#include "fbtft.h"
int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len)
{
struct spi_transfer t = {
.tx_buf = buf,
.len = len,
};
struct spi_message m;
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
if (!par->spi) {
dev_err(par->info->device,
"%s: par->spi is unexpectedly NULL\n", __func__);
return -1;
}
spi_message_init(&m);
if (par->txbuf.dma && buf == par->txbuf.buf) {
t.tx_dma = par->txbuf.dma;
m.is_dma_mapped = 1;
}
spi_message_add_tail(&t, &m);
return spi_sync(par->spi, &m);
}
EXPORT_SYMBOL(fbtft_write_spi);
/**
* fbtft_write_spi_emulate_9() - write SPI emulating 9-bit
* @par: Driver data
* @buf: Buffer to write
* @len: Length of buffer (must be divisible by 8)
*
* When 9-bit SPI is not available, this function can be used to emulate that.
* par->extra must hold a transformation buffer used for transfer.
*/
int fbtft_write_spi_emulate_9(struct fbtft_par *par, void *buf, size_t len)
{
u16 *src = buf;
u8 *dst = par->extra;
size_t size = len / 2;
size_t added = 0;
int bits, i, j;
u64 val, dc, tmp;
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
if (!par->extra) {
dev_err(par->info->device, "%s: error: par->extra is NULL\n",
__func__);
return -EINVAL;
}
if ((len % 8) != 0) {
dev_err(par->info->device,
"%s: error: len=%d must be divisible by 8\n",
__func__, len);
return -EINVAL;
}
for (i = 0; i < size; i += 8) {
tmp = 0;
bits = 63;
for (j = 0; j < 7; j++) {
dc = (*src & 0x0100) ? 1 : 0;
val = *src & 0x00FF;
tmp |= dc << bits;
bits -= 8;
tmp |= val << bits--;
src++;
}
tmp |= ((*src & 0x0100) ? 1 : 0);
*(u64 *)dst = cpu_to_be64(tmp);
dst += 8;
*dst++ = (u8)(*src++ & 0x00FF);
added++;
}
return spi_write(par->spi, par->extra, size + added);
}
EXPORT_SYMBOL(fbtft_write_spi_emulate_9);
int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len)
{
int ret;
u8 txbuf[32] = { 0, };
struct spi_transfer t = {
.speed_hz = 2000000,
.rx_buf = buf,
.len = len,
};
struct spi_message m;
if (!par->spi) {
dev_err(par->info->device,
"%s: par->spi is unexpectedly NULL\n", __func__);
return -ENODEV;
}
if (par->startbyte) {
if (len > 32) {
dev_err(par->info->device,
"%s: len=%d can't be larger than 32 when using 'startbyte'\n",
__func__, len);
return -EINVAL;
}
txbuf[0] = par->startbyte | 0x3;
t.tx_buf = txbuf;
fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8,
txbuf, len, "%s(len=%d) txbuf => ", __func__, len);
}
spi_message_init(&m);
spi_message_add_tail(&t, &m);
ret = spi_sync(par->spi, &m);
fbtft_par_dbg_hex(DEBUG_READ, par, par->info->device, u8, buf, len,
"%s(len=%d) buf <= ", __func__, len);
return ret;
}
EXPORT_SYMBOL(fbtft_read_spi);
#ifdef CONFIG_ARCH_BCM2708
/*
* Raspberry Pi
* - writing directly to the registers is 40-50% faster than
* optimized use of gpiolib
*/
#define GPIOSET(no, ishigh) \
do { \
if (ishigh) \
set |= (1 << (no)); \
else \
reset |= (1 << (no)); \
} while (0)
int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len)
{
unsigned int set = 0;
unsigned int reset = 0;
u8 data;
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
while (len--) {
data = *(u8 *) buf;
buf++;
/* Set data */
GPIOSET(par->gpio.db[0], (data&0x01));
GPIOSET(par->gpio.db[1], (data&0x02));
GPIOSET(par->gpio.db[2], (data&0x04));
GPIOSET(par->gpio.db[3], (data&0x08));
GPIOSET(par->gpio.db[4], (data&0x10));
GPIOSET(par->gpio.db[5], (data&0x20));
GPIOSET(par->gpio.db[6], (data&0x40));
GPIOSET(par->gpio.db[7], (data&0x80));
writel(set, __io_address(GPIO_BASE+0x1C));
writel(reset, __io_address(GPIO_BASE+0x28));
/* Pulse /WR low */
writel((1<<par->gpio.wr), __io_address(GPIO_BASE+0x28));
writel(0, __io_address(GPIO_BASE+0x28)); /* used as a delay */
writel((1<<par->gpio.wr), __io_address(GPIO_BASE+0x1C));
set = 0;
reset = 0;
}
return 0;
}
EXPORT_SYMBOL(fbtft_write_gpio8_wr);
int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len)
{
unsigned int set = 0;
unsigned int reset = 0;
u16 data;
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
while (len) {
len -= 2;
data = *(u16 *) buf;
buf += 2;
/* Start writing by pulling down /WR */
gpio_set_value(par->gpio.wr, 0);
/* Set data */
GPIOSET(par->gpio.db[0], (data&0x0001));
GPIOSET(par->gpio.db[1], (data&0x0002));
GPIOSET(par->gpio.db[2], (data&0x0004));
GPIOSET(par->gpio.db[3], (data&0x0008));
GPIOSET(par->gpio.db[4], (data&0x0010));
GPIOSET(par->gpio.db[5], (data&0x0020));
GPIOSET(par->gpio.db[6], (data&0x0040));
GPIOSET(par->gpio.db[7], (data&0x0080));
GPIOSET(par->gpio.db[8], (data&0x0100));
GPIOSET(par->gpio.db[9], (data&0x0200));
GPIOSET(par->gpio.db[10], (data&0x0400));
GPIOSET(par->gpio.db[11], (data&0x0800));
GPIOSET(par->gpio.db[12], (data&0x1000));
GPIOSET(par->gpio.db[13], (data&0x2000));
GPIOSET(par->gpio.db[14], (data&0x4000));
GPIOSET(par->gpio.db[15], (data&0x8000));
writel(set, __io_address(GPIO_BASE+0x1C));
writel(reset, __io_address(GPIO_BASE+0x28));
/* Pullup /WR */
gpio_set_value(par->gpio.wr, 1);
set = 0;
reset = 0;
}
return 0;
}
EXPORT_SYMBOL(fbtft_write_gpio16_wr);
int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len)
{
unsigned int set = 0;
unsigned int reset = 0;
u16 data;
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
while (len) {
len -= 2;
data = *(u16 *) buf;
buf += 2;
/* Start writing by pulling down /WR */
gpio_set_value(par->gpio.wr, 0);
/* Low byte */
GPIOSET(par->gpio.db[0], (data&0x0001));
GPIOSET(par->gpio.db[1], (data&0x0002));
GPIOSET(par->gpio.db[2], (data&0x0004));
GPIOSET(par->gpio.db[3], (data&0x0008));
GPIOSET(par->gpio.db[4], (data&0x0010));
GPIOSET(par->gpio.db[5], (data&0x0020));
GPIOSET(par->gpio.db[6], (data&0x0040));
GPIOSET(par->gpio.db[7], (data&0x0080));
writel(set, __io_address(GPIO_BASE+0x1C));
writel(reset, __io_address(GPIO_BASE+0x28));
/* Pulse 'latch' high */
gpio_set_value(par->gpio.latch, 1);
gpio_set_value(par->gpio.latch, 0);
/* High byte */
GPIOSET(par->gpio.db[0], (data&0x0100));
GPIOSET(par->gpio.db[1], (data&0x0200));
GPIOSET(par->gpio.db[2], (data&0x0400));
GPIOSET(par->gpio.db[3], (data&0x0800));
GPIOSET(par->gpio.db[4], (data&0x1000));
GPIOSET(par->gpio.db[5], (data&0x2000));
GPIOSET(par->gpio.db[6], (data&0x4000));
GPIOSET(par->gpio.db[7], (data&0x8000));
writel(set, __io_address(GPIO_BASE+0x1C));
writel(reset, __io_address(GPIO_BASE+0x28));
/* Pullup /WR */
gpio_set_value(par->gpio.wr, 1);
set = 0;
reset = 0;
}
return 0;
}
EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched);
#undef GPIOSET
#else
/*
* Optimized use of gpiolib is twice as fast as no optimization
* only one driver can use the optimized version at a time
*/
int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len)
{
u8 data;
int i;
#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
static u8 prev_data;
#endif
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
while (len--) {
data = *(u8 *) buf;
/* Start writing by pulling down /WR */
gpio_set_value(par->gpio.wr, 0);
/* Set data */
#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
if (data == prev_data) {
gpio_set_value(par->gpio.wr, 0); /* used as delay */
} else {
for (i = 0; i < 8; i++) {
if ((data & 1) != (prev_data & 1))
gpio_set_value(par->gpio.db[i],
(data & 1));
data >>= 1;
prev_data >>= 1;
}
}
#else
for (i = 0; i < 8; i++) {
gpio_set_value(par->gpio.db[i], (data & 1));
data >>= 1;
}
#endif
/* Pullup /WR */
gpio_set_value(par->gpio.wr, 1);
#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
prev_data = *(u8 *) buf;
#endif
buf++;
}
return 0;
}
EXPORT_SYMBOL(fbtft_write_gpio8_wr);
int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len)
{
u16 data;
int i;
#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
static u16 prev_data;
#endif
fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len,
"%s(len=%d): ", __func__, len);
while (len) {
data = *(u16 *) buf;
/* Start writing by pulling down /WR */
gpio_set_value(par->gpio.wr, 0);
/* Set data */
#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
if (data == prev_data) {
gpio_set_value(par->gpio.wr, 0); /* used as delay */
} else {
for (i = 0; i < 16; i++) {
if ((data & 1) != (prev_data & 1))
gpio_set_value(par->gpio.db[i],
(data & 1));
data >>= 1;
prev_data >>= 1;
}
}
#else
for (i = 0; i < 16; i++) {
gpio_set_value(par->gpio.db[i], (data & 1));
data >>= 1;
}
#endif
/* Pullup /WR */
gpio_set_value(par->gpio.wr, 1);
#ifndef DO_NOT_OPTIMIZE_FBTFT_WRITE_GPIO
prev_data = *(u16 *) buf;
#endif
buf += 2;
len -= 2;
}
return 0;
}
EXPORT_SYMBOL(fbtft_write_gpio16_wr);
int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len)
{
dev_err(par->info->device, "%s: function not implemented\n", __func__);
return -1;
}
EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched);
#endif /* CONFIG_ARCH_BCM2708 */

View File

@@ -0,0 +1,222 @@
#include "fbtft.h"
static int get_next_ulong(char **str_p, unsigned long *val, char *sep, int base)
{
char *p_val;
int ret;
if (!str_p || !(*str_p))
return -EINVAL;
p_val = strsep(str_p, sep);
if (!p_val)
return -EINVAL;
ret = kstrtoul(p_val, base, val);
if (ret)
return -EINVAL;
return 0;
}
int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves,
const char *str, int size)
{
char *str_p, *curve_p = NULL;
char *tmp;
unsigned long val = 0;
int ret = 0;
int curve_counter, value_counter;
fbtft_par_dbg(DEBUG_SYSFS, par, "%s() str=\n", __func__);
if (!str || !curves)
return -EINVAL;
fbtft_par_dbg(DEBUG_SYSFS, par, "%s\n", str);
tmp = kmalloc(size+1, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
memcpy(tmp, str, size+1);
/* replace optional separators */
str_p = tmp;
while (*str_p) {
if (*str_p == ',')
*str_p = ' ';
if (*str_p == ';')
*str_p = '\n';
str_p++;
}
str_p = strim(tmp);
curve_counter = 0;
while (str_p) {
if (curve_counter == par->gamma.num_curves) {
dev_err(par->info->device, "Gamma: Too many curves\n");
ret = -EINVAL;
goto out;
}
curve_p = strsep(&str_p, "\n");
value_counter = 0;
while (curve_p) {
if (value_counter == par->gamma.num_values) {
dev_err(par->info->device,
"Gamma: Too many values\n");
ret = -EINVAL;
goto out;
}
ret = get_next_ulong(&curve_p, &val, " ", 16);
if (ret)
goto out;
curves[curve_counter * par->gamma.num_values + value_counter] = val;
value_counter++;
}
if (value_counter != par->gamma.num_values) {
dev_err(par->info->device, "Gamma: Too few values\n");
ret = -EINVAL;
goto out;
}
curve_counter++;
}
if (curve_counter != par->gamma.num_curves) {
dev_err(par->info->device, "Gamma: Too few curves\n");
ret = -EINVAL;
goto out;
}
out:
kfree(tmp);
return ret;
}
static ssize_t
sprintf_gamma(struct fbtft_par *par, unsigned long *curves, char *buf)
{
ssize_t len = 0;
unsigned int i, j;
mutex_lock(&par->gamma.lock);
for (i = 0; i < par->gamma.num_curves; i++) {
for (j = 0; j < par->gamma.num_values; j++)
len += scnprintf(&buf[len], PAGE_SIZE,
"%04lx ", curves[i*par->gamma.num_values + j]);
buf[len-1] = '\n';
}
mutex_unlock(&par->gamma.lock);
return len;
}
static ssize_t store_gamma_curve(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct fbtft_par *par = fb_info->par;
unsigned long tmp_curves[FBTFT_GAMMA_MAX_VALUES_TOTAL];
int ret;
ret = fbtft_gamma_parse_str(par, tmp_curves, buf, count);
if (ret)
return ret;
ret = par->fbtftops.set_gamma(par, tmp_curves);
if (ret)
return ret;
mutex_lock(&par->gamma.lock);
memcpy(par->gamma.curves, tmp_curves,
par->gamma.num_curves * par->gamma.num_values * sizeof(tmp_curves[0]));
mutex_unlock(&par->gamma.lock);
return count;
}
static ssize_t show_gamma_curve(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct fbtft_par *par = fb_info->par;
return sprintf_gamma(par, par->gamma.curves, buf);
}
static struct device_attribute gamma_device_attrs[] = {
__ATTR(gamma, 0660, show_gamma_curve, store_gamma_curve),
};
void fbtft_expand_debug_value(unsigned long *debug)
{
switch (*debug & 0b111) {
case 1:
*debug |= DEBUG_LEVEL_1;
break;
case 2:
*debug |= DEBUG_LEVEL_2;
break;
case 3:
*debug |= DEBUG_LEVEL_3;
break;
case 4:
*debug |= DEBUG_LEVEL_4;
break;
case 5:
*debug |= DEBUG_LEVEL_5;
break;
case 6:
*debug |= DEBUG_LEVEL_6;
break;
case 7:
*debug = 0xFFFFFFFF;
break;
}
}
static ssize_t store_debug(struct device *device,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct fbtft_par *par = fb_info->par;
int ret;
ret = kstrtoul(buf, 10, &par->debug);
if (ret)
return ret;
fbtft_expand_debug_value(&par->debug);
return count;
}
static ssize_t show_debug(struct device *device,
struct device_attribute *attr, char *buf)
{
struct fb_info *fb_info = dev_get_drvdata(device);
struct fbtft_par *par = fb_info->par;
return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug);
}
static struct device_attribute debug_device_attr = \
__ATTR(debug, 0660, show_debug, store_debug);
void fbtft_sysfs_init(struct fbtft_par *par)
{
device_create_file(par->info->dev, &debug_device_attr);
if (par->gamma.curves && par->fbtftops.set_gamma)
device_create_file(par->info->dev, &gamma_device_attrs[0]);
}
void fbtft_sysfs_exit(struct fbtft_par *par)
{
device_remove_file(par->info->dev, &debug_device_attr);
if (par->gamma.curves && par->fbtftops.set_gamma)
device_remove_file(par->info->dev, &gamma_device_attrs[0]);
}

447
drivers/video/fbtft/fbtft.h Normal file
View File

@@ -0,0 +1,447 @@
/*
* 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>
#include <linux/spinlock.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#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_ONBOARD_BACKLIGHT 2
#define FBTFT_GPIO_NO_MATCH 0xFFFF
#define FBTFT_GPIO_NAME_SIZE 32
#define FBTFT_MAX_INIT_SEQUENCE 512
#define FBTFT_GAMMA_MAX_VALUES_TOTAL 128
#define FBTFT_OF_INIT_CMD BIT(24)
#define FBTFT_OF_INIT_DELAY BIT(25)
/**
* struct fbtft_gpio - Structure that holds one pinname to gpio mapping
* @name: pinname (reset, dc, etc.)
* @gpio: GPIO number
*
*/
struct fbtft_gpio {
char name[FBTFT_GPIO_NAME_SIZE];
unsigned gpio;
};
struct fbtft_par;
/**
* struct fbtft_ops - FBTFT operations structure
* @write: Writes to interface bus
* @read: Reads from interface bus
* @write_vmem: Writes video memory to display
* @write_reg: Writes to controller register
* @set_addr_win: Set the GRAM update window
* @reset: Reset the LCD controller
* @mkdirty: Marks display lines for update
* @update_display: Updates the display
* @init_display: Initializes the display
* @blank: Blank the display (optional)
* @request_gpios_match: Do pinname to gpio matching
* @request_gpios: Request gpios from the kernel
* @free_gpios: Free previously requested gpios
* @verify_gpios: Verify that necessary gpios is present (optional)
* @register_backlight: Used to register backlight device (optional)
* @unregister_backlight: Unregister backlight device (optional)
* @set_var: Configure LCD with values from variables like @rotate and @bgr
* (optional)
* @set_gamma: Set Gamma curve (optional)
*
* Most of these operations have default functions assigned to them in
* fbtft_framebuffer_alloc()
*/
struct fbtft_ops {
int (*write)(struct fbtft_par *par, void *buf, size_t len);
int (*read)(struct fbtft_par *par, void *buf, size_t len);
int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len);
void (*write_register)(struct fbtft_par *par, int 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,
unsigned start_line, unsigned end_line);
int (*init_display)(struct fbtft_par *par);
int (*blank)(struct fbtft_par *par, bool on);
unsigned long (*request_gpios_match)(struct fbtft_par *par,
const struct fbtft_gpio *gpio);
int (*request_gpios)(struct fbtft_par *par);
int (*verify_gpios)(struct fbtft_par *par);
void (*register_backlight)(struct fbtft_par *par);
void (*unregister_backlight)(struct fbtft_par *par);
int (*set_var)(struct fbtft_par *par);
int (*set_gamma)(struct fbtft_par *par, unsigned long *curves);
};
/**
* struct fbtft_display - Describes the display properties
* @width: Width of display in pixels
* @height: Height of display in pixels
* @regwidth: LCD Controller Register width in bits
* @buswidth: Display interface bus width in bits
* @backlight: Backlight type.
* @fbtftops: FBTFT operations provided by driver or device (platform_data)
* @bpp: Bits per pixel
* @fps: Frames per second
* @txbuflen: Size of transmit buffer
* @init_sequence: Pointer to LCD initialization array
* @gamma: String representation of Gamma curve(s)
* @gamma_num: Number of Gamma curves
* @gamma_len: Number of values per Gamma curve
* @debug: Initial debug value
*
* This structure is not stored by FBTFT except for init_sequence.
*/
struct fbtft_display {
unsigned width;
unsigned height;
unsigned regwidth;
unsigned buswidth;
unsigned backlight;
struct fbtft_ops fbtftops;
unsigned bpp;
unsigned fps;
int txbuflen;
int *init_sequence;
char *gamma;
int gamma_num;
int gamma_len;
unsigned long debug;
};
/**
* struct fbtft_platform_data - Passes display specific data to the driver
* @display: Display properties
* @gpios: Pointer to an array of piname to gpio mappings
* @rotate: Display rotation angle
* @bgr: LCD Controller BGR bit
* @fps: Frames per second (this will go away, use @fps in @fbtft_display)
* @txbuflen: Size of transmit buffer
* @startbyte: When set, enables use of Startbyte in transfers
* @gamma: String representation of Gamma curve(s)
* @extra: A way to pass extra info
*/
struct fbtft_platform_data {
struct fbtft_display display;
const struct fbtft_gpio *gpios;
unsigned rotate;
bool bgr;
unsigned fps;
int txbuflen;
u8 startbyte;
char *gamma;
void *extra;
};
/**
* struct fbtft_par - Main FBTFT data structure
*
* This structure holds all relevant data to operate the display
*
* See sourcefile for documentation since nested structs is not
* supported by kernel-doc.
*
*/
/* @spi: Set if it is a SPI device
* @pdev: Set if it is a platform device
* @info: Pointer to framebuffer fb_info structure
* @pdata: Pointer to platform data
* @ssbuf: Not used
* @pseudo_palette: Used by fb_set_colreg()
* @txbuf.buf: Transmit buffer
* @txbuf.len: Transmit buffer length
* @buf: Small buffer used when writing init data over SPI
* @startbyte: Used by some controllers when in SPI mode.
* Format: 6 bit Device id + RS bit + RW bit
* @fbtftops: FBTFT operations provided by driver or device (platform_data)
* @dirty_lock: Protects dirty_lines_start and dirty_lines_end
* @dirty_lines_start: Where to begin updating display
* @dirty_lines_end: Where to end updating display
* @gpio.reset: GPIO used to reset display
* @gpio.dc: Data/Command signal, also known as RS
* @gpio.rd: Read latching signal
* @gpio.wr: Write latching signal
* @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch
* @gpio.cs: LCD Chip Select with parallel interface bus
* @gpio.db[16]: Parallel databus
* @gpio.led[16]: Led control signals
* @gpio.aux[16]: Auxillary signals, not used by core
* @init_sequence: Pointer to LCD initialization array
* @gamma.lock: Mutex for Gamma curve locking
* @gamma.curves: Pointer to Gamma curve array
* @gamma.num_values: Number of values per Gamma curve
* @gamma.num_curves: Number of Gamma curves
* @debug: Pointer to debug value
* @current_debug:
* @first_update_done: Used to only time the first display update
* @update_time: Used to calculate 'fps' in debug output
* @bgr: BGR mode/\n
* @extra: Extra info needed by driver
*/
struct fbtft_par {
struct spi_device *spi;
struct platform_device *pdev;
struct fb_info *info;
struct fbtft_platform_data *pdata;
u16 *ssbuf;
u32 pseudo_palette[16];
struct {
void *buf;
dma_addr_t dma;
size_t len;
} txbuf;
u8 *buf;
u8 startbyte;
struct fbtft_ops fbtftops;
spinlock_t dirty_lock;
unsigned dirty_lines_start;
unsigned dirty_lines_end;
struct {
int reset;
int dc;
int rd;
int wr;
int latch;
int cs;
int db[16];
int led[16];
int aux[16];
} gpio;
int *init_sequence;
struct {
struct mutex lock;
unsigned long *curves;
int num_values;
int num_curves;
} gamma;
unsigned long debug;
bool first_update_done;
struct timespec update_time;
bool bgr;
void *extra;
};
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define write_reg(par, ...) \
do { \
par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \
} while (0)
/* fbtft-core.c */
extern void fbtft_dbg_hex(const struct device *dev,
int groupsize, void *buf, size_t len, const char *fmt, ...);
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);
extern void fbtft_register_backlight(struct fbtft_par *par);
extern void fbtft_unregister_backlight(struct fbtft_par *par);
extern int fbtft_init_display(struct fbtft_par *par);
extern int fbtft_probe_common(struct fbtft_display *display,
struct spi_device *sdev, struct platform_device *pdev);
extern int fbtft_remove_common(struct device *dev, struct fb_info *info);
/* fbtft-io.c */
extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_spi_emulate_9(struct fbtft_par *par,
void *buf, size_t len);
extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par,
void *buf, size_t len);
/* fbtft-bus.c */
extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len);
extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len);
extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len);
extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len);
extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...);
#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \
\
static int fbtft_driver_probe_spi(struct spi_device *spi) \
{ \
return fbtft_probe_common(_display, spi, NULL); \
} \
\
static int fbtft_driver_remove_spi(struct spi_device *spi) \
{ \
struct fb_info *info = spi_get_drvdata(spi); \
\
return fbtft_remove_common(&spi->dev, info); \
} \
\
static int fbtft_driver_probe_pdev(struct platform_device *pdev) \
{ \
return fbtft_probe_common(_display, NULL, pdev); \
} \
\
static int fbtft_driver_remove_pdev(struct platform_device *pdev) \
{ \
struct fb_info *info = platform_get_drvdata(pdev); \
\
return fbtft_remove_common(&pdev->dev, info); \
} \
\
static const struct of_device_id dt_ids[] = { \
{ .compatible = _compatible }, \
{}, \
}; \
\
MODULE_DEVICE_TABLE(of, dt_ids); \
\
\
static struct spi_driver fbtft_driver_spi_driver = { \
.driver = { \
.name = _name, \
.owner = THIS_MODULE, \
.of_match_table = of_match_ptr(dt_ids), \
}, \
.probe = fbtft_driver_probe_spi, \
.remove = fbtft_driver_remove_spi, \
}; \
\
static struct platform_driver fbtft_driver_platform_driver = { \
.driver = { \
.name = _name, \
.owner = THIS_MODULE, \
.of_match_table = of_match_ptr(dt_ids), \
}, \
.probe = fbtft_driver_probe_pdev, \
.remove = fbtft_driver_remove_pdev, \
}; \
\
static int __init fbtft_driver_module_init(void) \
{ \
int ret; \
\
ret = spi_register_driver(&fbtft_driver_spi_driver); \
if (ret < 0) \
return ret; \
return platform_driver_register(&fbtft_driver_platform_driver); \
} \
\
static void __exit fbtft_driver_module_exit(void) \
{ \
spi_unregister_driver(&fbtft_driver_spi_driver); \
platform_driver_unregister(&fbtft_driver_platform_driver); \
} \
\
module_init(fbtft_driver_module_init); \
module_exit(fbtft_driver_module_exit);
/* Debug macros */
/* shorthand debug levels */
#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS
#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE)
#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS)
#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK)
#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY)
#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5)
#define DEBUG_LEVEL_7 0xFFFFFFFF
#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3)
#define DEBUG_TIME_FIRST_UPDATE (1<<4)
#define DEBUG_TIME_EACH_UPDATE (1<<5)
#define DEBUG_DEFERRED_IO (1<<6)
#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7)
/* fbops */
#define DEBUG_FB_READ (1<<8)
#define DEBUG_FB_WRITE (1<<9)
#define DEBUG_FB_FILLRECT (1<<10)
#define DEBUG_FB_COPYAREA (1<<11)
#define DEBUG_FB_IMAGEBLIT (1<<12)
#define DEBUG_FB_SETCOLREG (1<<13)
#define DEBUG_FB_BLANK (1<<14)
#define DEBUG_SYSFS (1<<16)
/* fbtftops */
#define DEBUG_BACKLIGHT (1<<17)
#define DEBUG_READ (1<<18)
#define DEBUG_WRITE (1<<19)
#define DEBUG_WRITE_VMEM (1<<20)
#define DEBUG_WRITE_REGISTER (1<<21)
#define DEBUG_SET_ADDR_WIN (1<<22)
#define DEBUG_RESET (1<<23)
#define DEBUG_MKDIRTY (1<<24)
#define DEBUG_UPDATE_DISPLAY (1<<25)
#define DEBUG_INIT_DISPLAY (1<<26)
#define DEBUG_BLANK (1<<27)
#define DEBUG_REQUEST_GPIOS (1<<28)
#define DEBUG_FREE_GPIOS (1<<29)
#define DEBUG_REQUEST_GPIOS_MATCH (1<<30)
#define DEBUG_VERIFY_GPIOS (1<<31)
#define fbtft_init_dbg(dev, format, arg...) \
do { \
if (unlikely((dev)->platform_data && \
(((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \
dev_info(dev, format, ##arg); \
} while (0)
#define fbtft_par_dbg(level, par, format, arg...) \
do { \
if (unlikely(par->debug & level)) \
dev_info(par->info->device, format, ##arg); \
} while (0)
#define fbtft_dev_dbg(level, par, dev, format, arg...) \
do { \
if (unlikely(par->debug & level)) \
dev_info(dev, format, ##arg); \
} while (0)
#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \
do { \
if (unlikely(par->debug & level)) \
fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \
} while (0)
#endif /* __LINUX_FBTFT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,593 @@
/*
* Generic FB driver for TFT LCD displays
*
* 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/init.h>
#include <linux/vmalloc.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "fbtft.h"
#define DRVNAME "flexfb"
static char *chip = NULL;
module_param(chip, charp, 0);
MODULE_PARM_DESC(chip, "LCD controller");
static unsigned int width = 0;
module_param(width, uint, 0);
MODULE_PARM_DESC(width, "Display width");
static unsigned int height = 0;
module_param(height, uint, 0);
MODULE_PARM_DESC(height, "Display height");
static int init[512];
static int init_num = 0;
module_param_array(init, int, &init_num, 0);
MODULE_PARM_DESC(init, "Init sequence");
static unsigned int setaddrwin = 0;
module_param(setaddrwin, uint, 0);
MODULE_PARM_DESC(setaddrwin, "Which set_addr_win() implementation to use");
static unsigned int buswidth = 8;
module_param(buswidth, uint, 0);
MODULE_PARM_DESC(buswidth, "Width of databus (default: 8)");
static unsigned int regwidth = 8;
module_param(regwidth, uint, 0);
MODULE_PARM_DESC(regwidth, "Width of controller register (default: 8)");
static bool nobacklight = false;
module_param(nobacklight, bool, 0);
MODULE_PARM_DESC(nobacklight, "Turn off backlight functionality.");
static bool latched = false;
module_param(latched, bool, 0);
MODULE_PARM_DESC(latched, "Use with latched 16-bit databus");
static int *initp = NULL;
static int initp_num = 0;
/* default init sequences */
static int st7735r_init[] = { \
-1,0x01,-2,150,-1,0x11,-2,500,-1,0xB1,0x01,0x2C,0x2D,-1,0xB2,0x01,0x2C,0x2D,-1,0xB3,0x01,0x2C,0x2D,0x01,0x2C,0x2D, \
-1,0xB4,0x07,-1,0xC0,0xA2,0x02,0x84,-1,0xC1,0xC5,-1,0xC2,0x0A,0x00,-1,0xC3,0x8A,0x2A,-1,0xC4,0x8A,0xEE,-1,0xC5,0x0E, \
-1,0x20,-1,0x36,0xC0,-1,0x3A,0x05,-1,0xE0,0x0f,0x1a,0x0f,0x18,0x2f,0x28,0x20,0x22,0x1f,0x1b,0x23,0x37,0x00,0x07,0x02,0x10, \
-1,0xE1,0x0f,0x1b,0x0f,0x17,0x33,0x2c,0x29,0x2e,0x30,0x30,0x39,0x3f,0x00,0x07,0x03,0x10,-1,0x29,-2,100,-1,0x13,-2,10,-3 };
static int ssd1289_init[] = { \
-1,0x00,0x0001,-1,0x03,0xA8A4,-1,0x0C,0x0000,-1,0x0D,0x080C,-1,0x0E,0x2B00,-1,0x1E,0x00B7,-1,0x01,0x2B3F,-1,0x02,0x0600, \
-1,0x10,0x0000,-1,0x11,0x6070,-1,0x05,0x0000,-1,0x06,0x0000,-1,0x16,0xEF1C,-1,0x17,0x0003,-1,0x07,0x0233,-1,0x0B,0x0000, \
-1,0x0F,0x0000,-1,0x41,0x0000,-1,0x42,0x0000,-1,0x48,0x0000,-1,0x49,0x013F,-1,0x4A,0x0000,-1,0x4B,0x0000,-1,0x44,0xEF00, \
-1,0x45,0x0000,-1,0x46,0x013F,-1,0x30,0x0707,-1,0x31,0x0204,-1,0x32,0x0204,-1,0x33,0x0502,-1,0x34,0x0507,-1,0x35,0x0204, \
-1,0x36,0x0204,-1,0x37,0x0502,-1,0x3A,0x0302,-1,0x3B,0x0302,-1,0x23,0x0000,-1,0x24,0x0000,-1,0x25,0x8000,-1,0x4f,0x0000, \
-1,0x4e,0x0000,-1,0x22,-3 };
static int hx8340bn_init[] = { \
-1,0xC1,0xFF,0x83,0x40,-1,0x11,-2,150,-1,0xCA,0x70,0x00,0xD9,-1,0xB0,0x01,0x11, \
-1,0xC9,0x90,0x49,0x10,0x28,0x28,0x10,0x00,0x06,-2,20,-1,0xC2,0x60,0x71,0x01,0x0E,0x05,0x02,0x09,0x31,0x0A, \
-1,0xC3,0x67,0x30,0x61,0x17,0x48,0x07,0x05,0x33,-2,10,-1,0xB5,0x35,0x20,0x45,-1,0xB4,0x33,0x25,0x4C,-2,10, \
-1,0x3A,0x05,-1,0x29,-2,10,-3 };
static int ili9225_init[] = { \
-1,0x0001,0x011C,-1,0x0002,0x0100,-1,0x0003,0x1030,-1,0x0008,0x0808,-1,0x000C,0x0000,-1,0x000F,0x0A01,-1,0x0020,0x0000, \
-1,0x0021,0x0000,-2,50,-1,0x0010,0x0A00,-1,0x0011,0x1038,-2,50,-1,0x0012,0x1121,-1,0x0013,0x004E,-1,0x0014,0x676F, \
-1,0x0030,0x0000,-1,0x0031,0x00DB,-1,0x0032,0x0000,-1,0x0033,0x0000,-1,0x0034,0x00DB,-1,0x0035,0x0000,-1,0x0036,0x00AF, \
-1,0x0037,0x0000,-1,0x0038,0x00DB,-1,0x0039,0x0000,-1,0x0050,0x0000,-1,0x0051,0x060A,-1,0x0052,0x0D0A,-1,0x0053,0x0303, \
-1,0x0054,0x0A0D,-1,0x0055,0x0A06,-1,0x0056,0x0000,-1,0x0057,0x0303,-1,0x0058,0x0000,-1,0x0059,0x0000,-2,50, \
-1,0x0007,0x1017,-2,50,-3 };
static int ili9320_init[] = { \
-1,0x00E5,0x8000,-1,0x0000,0x0001,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000,-1,0x0008,0x0202, \
-1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000,-1,0x0011,0x0007, \
-1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x17B0,-1,0x0011,0x0031,-2,50,-1,0x0012,0x0138,-2,50,-1,0x0013,0x1800, \
-1,0x0029,0x0008,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000,-1,0x0031,0x0505,-1,0x0032,0x0004, \
-1,0x0035,0x0006,-1,0x0036,0x0707,-1,0x0037,0x0105,-1,0x0038,0x0002,-1,0x0039,0x0707,-1,0x003C,0x0704,-1,0x003D,0x0807, \
-1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0x2700,-1,0x0061,0x0001,-1,0x006A,0x0000, \
-1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000,-1,0x0085,0x0000,-1,0x0090,0x0010, \
-1,0x0092,0x0000,-1,0x0093,0x0003,-1,0x0095,0x0110,-1,0x0097,0x0000,-1,0x0098,0x0000,-1,0x0007,0x0173,-3 };
static int ili9325_init[] = { \
-1,0x00E3,0x3008,-1,0x00E7,0x0012,-1,0x00EF,0x1231,-1,0x0001,0x0100,-1,0x0002,0x0700,-1,0x0003,0x1030,-1,0x0004,0x0000, \
-1,0x0008,0x0207,-1,0x0009,0x0000,-1,0x000A,0x0000,-1,0x000C,0x0000,-1,0x000D,0x0000,-1,0x000F,0x0000,-1,0x0010,0x0000, \
-1,0x0011,0x0007,-1,0x0012,0x0000,-1,0x0013,0x0000,-2,200,-1,0x0010,0x1690,-1,0x0011,0x0223,-2,50,-1,0x0012,0x000D,-2,50, \
-1,0x0013,0x1200,-1,0x0029,0x000A,-1,0x002B,0x000C,-2,50,-1,0x0020,0x0000,-1,0x0021,0x0000,-1,0x0030,0x0000, \
-1,0x0031,0x0506,-1,0x0032,0x0104,-1,0x0035,0x0207,-1,0x0036,0x000F,-1,0x0037,0x0306,-1,0x0038,0x0102,-1,0x0039,0x0707, \
-1,0x003C,0x0702,-1,0x003D,0x1604,-1,0x0050,0x0000,-1,0x0051,0x00EF,-1,0x0052,0x0000,-1,0x0053,0x013F,-1,0x0060,0xA700, \
-1,0x0061,0x0001,-1,0x006A,0x0000,-1,0x0080,0x0000,-1,0x0081,0x0000,-1,0x0082,0x0000,-1,0x0083,0x0000,-1,0x0084,0x0000, \
-1,0x0085,0x0000,-1,0x0090,0x0010,-1,0x0092,0x0600,-1,0x0007,0x0133,-3 };
static int ili9341_init[] = { \
-1,0x28,-2,20,-1,0xCF,0x00,0x83,0x30,-1,0xED,0x64,0x03,0x12,0x81,-1,0xE8,0x85,0x01,0x79, \
-1,0xCB,0x39,0x2c,0x00,0x34,0x02,-1,0xF7,0x20,-1,0xEA,0x00,0x00,-1,0xC0,0x26,-1,0xC1,0x11, \
-1,0xC5,0x35,0x3E,-1,0xC7,0xBE,-1,0xB1,0x00,0x1B,-1,0xB6,0x0a,0x82,0x27,0x00,-1,0xB7,0x07, \
-1,0x3A,0x55,-1,0x36,0x48,-1,0x11,-2,120,-1,0x29,-2,20,-3 };
static int ssd1351_init[] = { -1,0xfd,0x12,-1,0xfd,0xb1,-1,0xae,-1,0xb3,0xf1,-1,0xca,0x7f,-1,0xa0,0x74, \
-1,0x15,0x00,0x7f,-1,0x75,0x00,0x7f,-1,0xa1,0x00,-1,0xa2,0x00,-1,0xb5,0x00, \
-1,0xab,0x01,-1,0xb1,0x32,-1,0xb4,0xa0,0xb5,0x55,-1,0xbb,0x17,-1,0xbe,0x05, \
-1,0xc1,0xc8,0x80,0xc8,-1,0xc7,0x0f,-1,0xb6,0x01,-1,0xa6,-1,0xaf,-3 };
/* ili9320, ili9325 */
static void flexfb_set_addr_win_1(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R20h = Horizontal GRAM Start Address */
/* R21h = Vertical GRAM Start Address */
case 0:
write_reg(par, 0x0020, xs);
write_reg(par, 0x0021, ys);
break;
case 180:
write_reg(par, 0x0020, width - 1 - xs);
write_reg(par, 0x0021, height - 1 - ys);
break;
case 270:
write_reg(par, 0x0020, width - 1 - ys);
write_reg(par, 0x0021, xs);
break;
case 90:
write_reg(par, 0x0020, ys);
write_reg(par, 0x0021, height - 1 - xs);
break;
}
write_reg(par, 0x0022); /* Write Data to GRAM */
}
/* ssd1289 */
static void flexfb_set_addr_win_2(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
switch (par->info->var.rotate) {
/* R4Eh - Set GDDRAM X address counter */
/* R4Fh - Set GDDRAM Y address counter */
case 0:
write_reg(par, 0x4e, xs);
write_reg(par, 0x4f, ys);
break;
case 180:
write_reg(par, 0x4e, par->info->var.xres - 1 - xs);
write_reg(par, 0x4f, par->info->var.yres - 1 - ys);
break;
case 270:
write_reg(par, 0x4e, par->info->var.yres - 1 - ys);
write_reg(par, 0x4f, xs);
break;
case 90:
write_reg(par, 0x4e, ys);
write_reg(par, 0x4f, par->info->var.xres - 1 - xs);
break;
}
/* R22h - RAM data write */
write_reg(par, 0x22, 0);
}
/* ssd1351 */
static void set_addr_win_3(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
write_reg(par, 0x15, xs, xe);
write_reg(par, 0x75, ys, ye);
write_reg(par, 0x5C);
}
static int flexfb_verify_gpios_dc(struct fbtft_par *par)
{
fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__);
if (par->gpio.dc < 0) {
dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n");
return -EINVAL;
}
return 0;
}
static int flexfb_verify_gpios_db(struct fbtft_par *par)
{
int i;
int num_db = buswidth;
fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__);
if (par->gpio.dc < 0) {
dev_err(par->info->device, "Missing info about 'dc' gpio. Aborting.\n");
return -EINVAL;
}
if (par->gpio.wr < 0) {
dev_err(par->info->device, "Missing info about 'wr' gpio. Aborting.\n");
return -EINVAL;
}
if (latched && (par->gpio.latch < 0)) {
dev_err(par->info->device, "Missing info about 'latch' gpio. Aborting.\n");
return -EINVAL;
}
if (latched)
num_db=buswidth/2;
for (i=0;i < num_db;i++) {
if (par->gpio.db[i] < 0) {
dev_err(par->info->device, "Missing info about 'db%02d' gpio. Aborting.\n", i);
return -EINVAL;
}
}
return 0;
}
static struct fbtft_display flex_display = { };
static int flexfb_probe_common(struct spi_device *sdev, struct platform_device *pdev)
{
struct device *dev;
struct fb_info *info;
struct fbtft_par *par;
int ret;
initp = init;
initp_num = init_num;
if (sdev)
dev = &sdev->dev;
else
dev = &pdev->dev;
fbtft_init_dbg(dev, "%s(%s)\n", __func__, sdev ? "'SPI device'" : "'Platform device'");
if (chip) {
if (!strcmp(chip, "st7735r")) {
if (!width)
width = 128;
if (!height)
height = 160;
if (init_num == 0) {
initp = st7735r_init;
initp_num = ARRAY_SIZE(st7735r_init);
}
} else if (!strcmp(chip, "hx8340bn")) {
if (!width)
width = 176;
if (!height)
height = 220;
setaddrwin = 0;
if (init_num == 0) {
initp = hx8340bn_init;
initp_num = ARRAY_SIZE(hx8340bn_init);
}
} else if (!strcmp(chip, "ili9225")) {
if (!width)
width = 176;
if (!height)
height = 220;
setaddrwin = 0;
regwidth = 16;
if (init_num == 0) {
initp = ili9225_init;
initp_num = ARRAY_SIZE(ili9225_init);
}
} else if (!strcmp(chip, "ili9320")) {
if (!width)
width = 240;
if (!height)
height = 320;
setaddrwin = 1;
regwidth = 16;
if (init_num == 0) {
initp = ili9320_init;
initp_num = ARRAY_SIZE(ili9320_init);
}
} else if (!strcmp(chip, "ili9325")) {
if (!width)
width = 240;
if (!height)
height = 320;
setaddrwin = 1;
regwidth = 16;
if (init_num == 0) {
initp = ili9325_init;
initp_num = ARRAY_SIZE(ili9325_init);
}
} else if (!strcmp(chip, "ili9341")) {
if (!width)
width = 240;
if (!height)
height = 320;
setaddrwin = 0;
regwidth = 8;
if (init_num == 0) {
initp = ili9341_init;
initp_num = ARRAY_SIZE(ili9341_init);
}
} else if (!strcmp(chip, "ssd1289")) {
if (!width)
width = 240;
if (!height)
height = 320;
setaddrwin = 2;
regwidth = 16;
if (init_num == 0) {
initp = ssd1289_init;
initp_num = ARRAY_SIZE(ssd1289_init);
}
} else if (!strcmp(chip, "ssd1351")) {
if (!width)
width = 128;
if (!height)
height = 128;
setaddrwin = 3;
if (init_num == 0) {
initp = ssd1351_init;
initp_num = ARRAY_SIZE(ssd1351_init);
}
} else {
dev_err(dev, "chip=%s is not supported\n", chip);
return -EINVAL;
}
}
if (width == 0 || height == 0) {
dev_err(dev, "argument(s) missing: width and height has to be set.\n");
return -EINVAL;
}
flex_display.width = width;
flex_display.height = height;
fbtft_init_dbg(dev, "Display resolution: %dx%d\n", width, height);
fbtft_init_dbg(dev, "chip = %s\n", chip ? chip : "not set");
fbtft_init_dbg(dev, "setaddrwin = %d\n", setaddrwin);
fbtft_init_dbg(dev, "regwidth = %d\n", regwidth);
fbtft_init_dbg(dev, "buswidth = %d\n", buswidth);
info = fbtft_framebuffer_alloc(&flex_display, dev);
if (!info)
return -ENOMEM;
par = info->par;
if (sdev)
par->spi = sdev;
else
par->pdev = pdev;
if (!par->init_sequence)
par->init_sequence = initp;
par->fbtftops.init_display = fbtft_init_display;
/* registerwrite functions */
switch (regwidth) {
case 8:
par->fbtftops.write_register = fbtft_write_reg8_bus8;
break;
case 16:
par->fbtftops.write_register = fbtft_write_reg16_bus8;
break;
default:
dev_err(dev, "argument 'regwidth': %d is not supported.\n", regwidth);
return -EINVAL;
}
/* bus functions */
if (sdev) {
par->fbtftops.write = fbtft_write_spi;
switch (buswidth) {
case 8:
par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
if (!par->startbyte)
par->fbtftops.verify_gpios = flexfb_verify_gpios_dc;
break;
case 9:
if (regwidth == 16) {
dev_err(dev, "argument 'regwidth': %d is not supported with buswidth=%d and SPI.\n", regwidth, buswidth);
return -EINVAL;
}
par->fbtftops.write_register = fbtft_write_reg8_bus9;
par->fbtftops.write_vmem = fbtft_write_vmem16_bus9;
sdev->bits_per_word=9;
ret = sdev->master->setup(sdev);
if (ret) {
dev_warn(dev,
"9-bit SPI not available, emulating using 8-bit.\n");
sdev->bits_per_word = 8;
ret = sdev->master->setup(sdev);
if (ret)
goto out_release;
/* allocate buffer with room for dc bits */
par->extra = devm_kzalloc(par->info->device,
par->txbuf.len + (par->txbuf.len / 8) + 8,
GFP_KERNEL);
if (!par->extra) {
ret = -ENOMEM;
goto out_release;
}
par->fbtftops.write = fbtft_write_spi_emulate_9;
}
break;
default:
dev_err(dev, "argument 'buswidth': %d is not supported with SPI.\n", buswidth);
return -EINVAL;
}
} else {
par->fbtftops.verify_gpios = flexfb_verify_gpios_db;
switch (buswidth) {
case 8:
par->fbtftops.write = fbtft_write_gpio8_wr;
par->fbtftops.write_vmem = fbtft_write_vmem16_bus8;
break;
case 16:
par->fbtftops.write_register = fbtft_write_reg16_bus16;
if (latched)
par->fbtftops.write = fbtft_write_gpio16_wr_latched;
else
par->fbtftops.write = fbtft_write_gpio16_wr;
par->fbtftops.write_vmem = fbtft_write_vmem16_bus16;
break;
default:
dev_err(dev, "argument 'buswidth': %d is not supported with parallel.\n", buswidth);
return -EINVAL;
}
}
/* set_addr_win function */
switch (setaddrwin) {
case 0:
/* use default */
break;
case 1:
par->fbtftops.set_addr_win = flexfb_set_addr_win_1;
break;
case 2:
par->fbtftops.set_addr_win = flexfb_set_addr_win_2;
break;
case 3:
par->fbtftops.set_addr_win = set_addr_win_3;
break;
default:
dev_err(dev, "argument 'setaddrwin': unknown value %d.\n", setaddrwin);
return -EINVAL;
}
if (!nobacklight)
par->fbtftops.register_backlight = fbtft_register_backlight;
ret = fbtft_register_framebuffer(info);
if (ret < 0)
goto out_release;
return 0;
out_release:
fbtft_framebuffer_release(info);
return ret;
}
static int flexfb_remove_common(struct device *dev, struct fb_info *info)
{
struct fbtft_par *par;
if (!info)
return -EINVAL;
par = info->par;
if (par)
fbtft_par_dbg(DEBUG_DRIVER_INIT_FUNCTIONS, par,
"%s()\n", __func__);
fbtft_unregister_framebuffer(info);
fbtft_framebuffer_release(info);
return 0;
}
static int flexfb_probe_spi(struct spi_device *spi)
{
return flexfb_probe_common(spi, NULL);
}
static int flexfb_remove_spi(struct spi_device *spi)
{
struct fb_info *info = spi_get_drvdata(spi);
return flexfb_remove_common(&spi->dev, info);
}
static int flexfb_probe_pdev(struct platform_device *pdev)
{
return flexfb_probe_common(NULL, pdev);
}
static int flexfb_remove_pdev(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
return flexfb_remove_common(&pdev->dev, info);
}
static struct spi_driver flexfb_spi_driver = {
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
.probe = flexfb_probe_spi,
.remove = flexfb_remove_spi,
};
static const struct platform_device_id flexfb_platform_ids[] = {
{ "flexpfb", 0 },
{ },
};
static struct platform_driver flexfb_platform_driver = {
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,
},
.id_table = flexfb_platform_ids,
.probe = flexfb_probe_pdev,
.remove = flexfb_remove_pdev,
};
static int __init flexfb_init(void)
{
int ret, ret2;
ret = spi_register_driver(&flexfb_spi_driver);
ret2 = platform_driver_register(&flexfb_platform_driver);
if (ret < 0)
return ret;
return ret2;
}
static void __exit flexfb_exit(void)
{
spi_unregister_driver(&flexfb_spi_driver);
platform_driver_unregister(&flexfb_platform_driver);
}
/* ------------------------------------------------------------------------- */
module_init(flexfb_init);
module_exit(flexfb_exit);
MODULE_DESCRIPTION("Generic FB driver for TFT LCD displays");
MODULE_AUTHOR("Noralf Tronnes");
MODULE_LICENSE("GPL");

447
include/linux/fbtft.h Normal file
View File

@@ -0,0 +1,447 @@
/*
* 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>
#include <linux/spinlock.h>
#include <linux/spi/spi.h>
#include <linux/platform_device.h>
#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_ONBOARD_BACKLIGHT 2
#define FBTFT_GPIO_NO_MATCH 0xFFFF
#define FBTFT_GPIO_NAME_SIZE 32
#define FBTFT_MAX_INIT_SEQUENCE 512
#define FBTFT_GAMMA_MAX_VALUES_TOTAL 128
#define FBTFT_OF_INIT_CMD BIT(24)
#define FBTFT_OF_INIT_DELAY BIT(25)
/**
* struct fbtft_gpio - Structure that holds one pinname to gpio mapping
* @name: pinname (reset, dc, etc.)
* @gpio: GPIO number
*
*/
struct fbtft_gpio {
char name[FBTFT_GPIO_NAME_SIZE];
unsigned gpio;
};
struct fbtft_par;
/**
* struct fbtft_ops - FBTFT operations structure
* @write: Writes to interface bus
* @read: Reads from interface bus
* @write_vmem: Writes video memory to display
* @write_reg: Writes to controller register
* @set_addr_win: Set the GRAM update window
* @reset: Reset the LCD controller
* @mkdirty: Marks display lines for update
* @update_display: Updates the display
* @init_display: Initializes the display
* @blank: Blank the display (optional)
* @request_gpios_match: Do pinname to gpio matching
* @request_gpios: Request gpios from the kernel
* @free_gpios: Free previously requested gpios
* @verify_gpios: Verify that necessary gpios is present (optional)
* @register_backlight: Used to register backlight device (optional)
* @unregister_backlight: Unregister backlight device (optional)
* @set_var: Configure LCD with values from variables like @rotate and @bgr
* (optional)
* @set_gamma: Set Gamma curve (optional)
*
* Most of these operations have default functions assigned to them in
* fbtft_framebuffer_alloc()
*/
struct fbtft_ops {
int (*write)(struct fbtft_par *par, void *buf, size_t len);
int (*read)(struct fbtft_par *par, void *buf, size_t len);
int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len);
void (*write_register)(struct fbtft_par *par, int 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,
unsigned start_line, unsigned end_line);
int (*init_display)(struct fbtft_par *par);
int (*blank)(struct fbtft_par *par, bool on);
unsigned long (*request_gpios_match)(struct fbtft_par *par,
const struct fbtft_gpio *gpio);
int (*request_gpios)(struct fbtft_par *par);
int (*verify_gpios)(struct fbtft_par *par);
void (*register_backlight)(struct fbtft_par *par);
void (*unregister_backlight)(struct fbtft_par *par);
int (*set_var)(struct fbtft_par *par);
int (*set_gamma)(struct fbtft_par *par, unsigned long *curves);
};
/**
* struct fbtft_display - Describes the display properties
* @width: Width of display in pixels
* @height: Height of display in pixels
* @regwidth: LCD Controller Register width in bits
* @buswidth: Display interface bus width in bits
* @backlight: Backlight type.
* @fbtftops: FBTFT operations provided by driver or device (platform_data)
* @bpp: Bits per pixel
* @fps: Frames per second
* @txbuflen: Size of transmit buffer
* @init_sequence: Pointer to LCD initialization array
* @gamma: String representation of Gamma curve(s)
* @gamma_num: Number of Gamma curves
* @gamma_len: Number of values per Gamma curve
* @debug: Initial debug value
*
* This structure is not stored by FBTFT except for init_sequence.
*/
struct fbtft_display {
unsigned width;
unsigned height;
unsigned regwidth;
unsigned buswidth;
unsigned backlight;
struct fbtft_ops fbtftops;
unsigned bpp;
unsigned fps;
int txbuflen;
int *init_sequence;
char *gamma;
int gamma_num;
int gamma_len;
unsigned long debug;
};
/**
* struct fbtft_platform_data - Passes display specific data to the driver
* @display: Display properties
* @gpios: Pointer to an array of piname to gpio mappings
* @rotate: Display rotation angle
* @bgr: LCD Controller BGR bit
* @fps: Frames per second (this will go away, use @fps in @fbtft_display)
* @txbuflen: Size of transmit buffer
* @startbyte: When set, enables use of Startbyte in transfers
* @gamma: String representation of Gamma curve(s)
* @extra: A way to pass extra info
*/
struct fbtft_platform_data {
struct fbtft_display display;
const struct fbtft_gpio *gpios;
unsigned rotate;
bool bgr;
unsigned fps;
int txbuflen;
u8 startbyte;
char *gamma;
void *extra;
};
/**
* struct fbtft_par - Main FBTFT data structure
*
* This structure holds all relevant data to operate the display
*
* See sourcefile for documentation since nested structs is not
* supported by kernel-doc.
*
*/
/* @spi: Set if it is a SPI device
* @pdev: Set if it is a platform device
* @info: Pointer to framebuffer fb_info structure
* @pdata: Pointer to platform data
* @ssbuf: Not used
* @pseudo_palette: Used by fb_set_colreg()
* @txbuf.buf: Transmit buffer
* @txbuf.len: Transmit buffer length
* @buf: Small buffer used when writing init data over SPI
* @startbyte: Used by some controllers when in SPI mode.
* Format: 6 bit Device id + RS bit + RW bit
* @fbtftops: FBTFT operations provided by driver or device (platform_data)
* @dirty_lock: Protects dirty_lines_start and dirty_lines_end
* @dirty_lines_start: Where to begin updating display
* @dirty_lines_end: Where to end updating display
* @gpio.reset: GPIO used to reset display
* @gpio.dc: Data/Command signal, also known as RS
* @gpio.rd: Read latching signal
* @gpio.wr: Write latching signal
* @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch
* @gpio.cs: LCD Chip Select with parallel interface bus
* @gpio.db[16]: Parallel databus
* @gpio.led[16]: Led control signals
* @gpio.aux[16]: Auxillary signals, not used by core
* @init_sequence: Pointer to LCD initialization array
* @gamma.lock: Mutex for Gamma curve locking
* @gamma.curves: Pointer to Gamma curve array
* @gamma.num_values: Number of values per Gamma curve
* @gamma.num_curves: Number of Gamma curves
* @debug: Pointer to debug value
* @current_debug:
* @first_update_done: Used to only time the first display update
* @update_time: Used to calculate 'fps' in debug output
* @bgr: BGR mode/\n
* @extra: Extra info needed by driver
*/
struct fbtft_par {
struct spi_device *spi;
struct platform_device *pdev;
struct fb_info *info;
struct fbtft_platform_data *pdata;
u16 *ssbuf;
u32 pseudo_palette[16];
struct {
void *buf;
dma_addr_t dma;
size_t len;
} txbuf;
u8 *buf;
u8 startbyte;
struct fbtft_ops fbtftops;
spinlock_t dirty_lock;
unsigned dirty_lines_start;
unsigned dirty_lines_end;
struct {
int reset;
int dc;
int rd;
int wr;
int latch;
int cs;
int db[16];
int led[16];
int aux[16];
} gpio;
int *init_sequence;
struct {
struct mutex lock;
unsigned long *curves;
int num_values;
int num_curves;
} gamma;
unsigned long debug;
bool first_update_done;
struct timespec update_time;
bool bgr;
void *extra;
};
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define write_reg(par, ...) \
do { \
par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \
} while (0)
/* fbtft-core.c */
extern void fbtft_dbg_hex(const struct device *dev,
int groupsize, void *buf, size_t len, const char *fmt, ...);
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);
extern void fbtft_register_backlight(struct fbtft_par *par);
extern void fbtft_unregister_backlight(struct fbtft_par *par);
extern int fbtft_init_display(struct fbtft_par *par);
extern int fbtft_probe_common(struct fbtft_display *display,
struct spi_device *sdev, struct platform_device *pdev);
extern int fbtft_remove_common(struct device *dev, struct fb_info *info);
/* fbtft-io.c */
extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_spi_emulate_9(struct fbtft_par *par,
void *buf, size_t len);
extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len);
extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par,
void *buf, size_t len);
/* fbtft-bus.c */
extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len);
extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len);
extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len);
extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len);
extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...);
extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...);
#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \
\
static int fbtft_driver_probe_spi(struct spi_device *spi) \
{ \
return fbtft_probe_common(_display, spi, NULL); \
} \
\
static int fbtft_driver_remove_spi(struct spi_device *spi) \
{ \
struct fb_info *info = spi_get_drvdata(spi); \
\
return fbtft_remove_common(&spi->dev, info); \
} \
\
static int fbtft_driver_probe_pdev(struct platform_device *pdev) \
{ \
return fbtft_probe_common(_display, NULL, pdev); \
} \
\
static int fbtft_driver_remove_pdev(struct platform_device *pdev) \
{ \
struct fb_info *info = platform_get_drvdata(pdev); \
\
return fbtft_remove_common(&pdev->dev, info); \
} \
\
static const struct of_device_id dt_ids[] = { \
{ .compatible = _compatible }, \
{}, \
}; \
\
MODULE_DEVICE_TABLE(of, dt_ids); \
\
\
static struct spi_driver fbtft_driver_spi_driver = { \
.driver = { \
.name = _name, \
.owner = THIS_MODULE, \
.of_match_table = of_match_ptr(dt_ids), \
}, \
.probe = fbtft_driver_probe_spi, \
.remove = fbtft_driver_remove_spi, \
}; \
\
static struct platform_driver fbtft_driver_platform_driver = { \
.driver = { \
.name = _name, \
.owner = THIS_MODULE, \
.of_match_table = of_match_ptr(dt_ids), \
}, \
.probe = fbtft_driver_probe_pdev, \
.remove = fbtft_driver_remove_pdev, \
}; \
\
static int __init fbtft_driver_module_init(void) \
{ \
int ret; \
\
ret = spi_register_driver(&fbtft_driver_spi_driver); \
if (ret < 0) \
return ret; \
return platform_driver_register(&fbtft_driver_platform_driver); \
} \
\
static void __exit fbtft_driver_module_exit(void) \
{ \
spi_unregister_driver(&fbtft_driver_spi_driver); \
platform_driver_unregister(&fbtft_driver_platform_driver); \
} \
\
module_init(fbtft_driver_module_init); \
module_exit(fbtft_driver_module_exit);
/* Debug macros */
/* shorthand debug levels */
#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS
#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE)
#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS)
#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK)
#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY)
#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5)
#define DEBUG_LEVEL_7 0xFFFFFFFF
#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3)
#define DEBUG_TIME_FIRST_UPDATE (1<<4)
#define DEBUG_TIME_EACH_UPDATE (1<<5)
#define DEBUG_DEFERRED_IO (1<<6)
#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7)
/* fbops */
#define DEBUG_FB_READ (1<<8)
#define DEBUG_FB_WRITE (1<<9)
#define DEBUG_FB_FILLRECT (1<<10)
#define DEBUG_FB_COPYAREA (1<<11)
#define DEBUG_FB_IMAGEBLIT (1<<12)
#define DEBUG_FB_SETCOLREG (1<<13)
#define DEBUG_FB_BLANK (1<<14)
#define DEBUG_SYSFS (1<<16)
/* fbtftops */
#define DEBUG_BACKLIGHT (1<<17)
#define DEBUG_READ (1<<18)
#define DEBUG_WRITE (1<<19)
#define DEBUG_WRITE_VMEM (1<<20)
#define DEBUG_WRITE_REGISTER (1<<21)
#define DEBUG_SET_ADDR_WIN (1<<22)
#define DEBUG_RESET (1<<23)
#define DEBUG_MKDIRTY (1<<24)
#define DEBUG_UPDATE_DISPLAY (1<<25)
#define DEBUG_INIT_DISPLAY (1<<26)
#define DEBUG_BLANK (1<<27)
#define DEBUG_REQUEST_GPIOS (1<<28)
#define DEBUG_FREE_GPIOS (1<<29)
#define DEBUG_REQUEST_GPIOS_MATCH (1<<30)
#define DEBUG_VERIFY_GPIOS (1<<31)
#define fbtft_init_dbg(dev, format, arg...) \
do { \
if (unlikely((dev)->platform_data && \
(((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \
dev_info(dev, format, ##arg); \
} while (0)
#define fbtft_par_dbg(level, par, format, arg...) \
do { \
if (unlikely(par->debug & level)) \
dev_info(par->info->device, format, ##arg); \
} while (0)
#define fbtft_dev_dbg(level, par, dev, format, arg...) \
do { \
if (unlikely(par->debug & level)) \
dev_info(dev, format, ##arg); \
} while (0)
#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \
do { \
if (unlikely(par->debug & level)) \
fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \
} while (0)
#endif /* __LINUX_FBTFT_H */