mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-03 09:41:54 +09:00
video/fbtft: Add notro fbtft device driver
Change-Id: Ibcdc3122a24e7a10bdc05def6e5f70cee20568ee
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
87
drivers/video/fbtft/.gitignore
vendored
Normal 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
169
drivers/video/fbtft/Kconfig
Normal 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
|
||||
60
drivers/video/fbtft/Makefile
Normal file
60
drivers/video/fbtft/Makefile
Normal 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
|
||||
32
drivers/video/fbtft/README
Normal file
32
drivers/video/fbtft/README
Normal 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/
|
||||
414
drivers/video/fbtft/dts/rpi.dts
Normal file
414
drivers/video/fbtft/dts/rpi.dts
Normal 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>;
|
||||
};
|
||||
};
|
||||
462
drivers/video/fbtft/fb_agm1264k-fl.c
Normal file
462
drivers/video/fbtft/fb_agm1264k-fl.c
Normal 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");
|
||||
193
drivers/video/fbtft/fb_bd663474.c
Normal file
193
drivers/video/fbtft/fb_bd663474.c
Normal 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");
|
||||
229
drivers/video/fbtft/fb_hx8340bn.c
Normal file
229
drivers/video/fbtft/fb_hx8340bn.c
Normal 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");
|
||||
181
drivers/video/fbtft/fb_hx8347d.c
Normal file
181
drivers/video/fbtft/fb_hx8347d.c
Normal 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");
|
||||
166
drivers/video/fbtft/fb_hx8353d.c
Normal file
166
drivers/video/fbtft/fb_hx8353d.c
Normal 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");
|
||||
234
drivers/video/fbtft/fb_ili9320.c
Normal file
234
drivers/video/fbtft/fb_ili9320.c
Normal 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");
|
||||
291
drivers/video/fbtft/fb_ili9325.c
Normal file
291
drivers/video/fbtft/fb_ili9325.c
Normal 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");
|
||||
163
drivers/video/fbtft/fb_ili9340.c
Normal file
163
drivers/video/fbtft/fb_ili9340.c
Normal 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");
|
||||
179
drivers/video/fbtft/fb_ili9341.c
Normal file
179
drivers/video/fbtft/fb_ili9341.c
Normal 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");
|
||||
117
drivers/video/fbtft/fb_ili9481.c
Normal file
117
drivers/video/fbtft/fb_ili9481.c
Normal 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");
|
||||
121
drivers/video/fbtft/fb_ili9486.c
Normal file
121
drivers/video/fbtft/fb_ili9486.c
Normal 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");
|
||||
177
drivers/video/fbtft/fb_pcd8544.c
Normal file
177
drivers/video/fbtft/fb_pcd8544.c
Normal 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");
|
||||
331
drivers/video/fbtft/fb_ra8875.c
Normal file
331
drivers/video/fbtft/fb_ra8875.c
Normal 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");
|
||||
168
drivers/video/fbtft/fb_s6d02a1.c
Normal file
168
drivers/video/fbtft/fb_s6d02a1.c
Normal 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");
|
||||
208
drivers/video/fbtft/fb_s6d1121.c
Normal file
208
drivers/video/fbtft/fb_s6d1121.c
Normal 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");
|
||||
206
drivers/video/fbtft/fb_ssd1289.c
Normal file
206
drivers/video/fbtft/fb_ssd1289.c
Normal 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");
|
||||
229
drivers/video/fbtft/fb_ssd1306.c
Normal file
229
drivers/video/fbtft/fb_ssd1306.c
Normal 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");
|
||||
205
drivers/video/fbtft/fb_ssd1331.c
Normal file
205
drivers/video/fbtft/fb_ssd1331.c
Normal 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");
|
||||
258
drivers/video/fbtft/fb_ssd1351.c
Normal file
258
drivers/video/fbtft/fb_ssd1351.c
Normal 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");
|
||||
195
drivers/video/fbtft/fb_st7735r.c
Normal file
195
drivers/video/fbtft/fb_st7735r.c
Normal 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");
|
||||
124
drivers/video/fbtft/fb_tinylcd.c
Normal file
124
drivers/video/fbtft/fb_tinylcd.c
Normal 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");
|
||||
176
drivers/video/fbtft/fb_tls8204.c
Normal file
176
drivers/video/fbtft/fb_tls8204.c
Normal 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");
|
||||
210
drivers/video/fbtft/fb_uc1701.c
Normal file
210
drivers/video/fbtft/fb_uc1701.c
Normal 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");
|
||||
206
drivers/video/fbtft/fb_upd161704.c
Normal file
206
drivers/video/fbtft/fb_upd161704.c
Normal 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");
|
||||
324
drivers/video/fbtft/fb_watterott.c
Normal file
324
drivers/video/fbtft/fb_watterott.c
Normal 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");
|
||||
256
drivers/video/fbtft/fbtft-bus.c
Normal file
256
drivers/video/fbtft/fbtft-bus.c
Normal 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);
|
||||
1516
drivers/video/fbtft/fbtft-core.c
Normal file
1516
drivers/video/fbtft/fbtft-core.c
Normal file
File diff suppressed because it is too large
Load Diff
409
drivers/video/fbtft/fbtft-io.c
Normal file
409
drivers/video/fbtft/fbtft-io.c
Normal 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 */
|
||||
222
drivers/video/fbtft/fbtft-sysfs.c
Normal file
222
drivers/video/fbtft/fbtft-sysfs.c
Normal 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
447
drivers/video/fbtft/fbtft.h
Normal 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 */
|
||||
1444
drivers/video/fbtft/fbtft_device.c
Normal file
1444
drivers/video/fbtft/fbtft_device.c
Normal file
File diff suppressed because it is too large
Load Diff
593
drivers/video/fbtft/flexfb.c
Normal file
593
drivers/video/fbtft/flexfb.c
Normal 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
447
include/linux/fbtft.h
Normal 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 */
|
||||
Reference in New Issue
Block a user