diff --git a/build-all.sh b/build-all.sh index a541134bed..6a9c89c6e0 100644 --- a/build-all.sh +++ b/build-all.sh @@ -12,6 +12,17 @@ IFS=";" START=0 OLDFAMILY="" +#BUILD_ALL="demo" + +# vaid options for automatic building and menu selection +# +# build 0 = don't build +# build 1 = old kernel +# build 2 = next kernel +# build 3 = both kernels +# build 4 = dev kernel +# build 5 = next and dev kernels +# build 6 = legacy and next and dev kernel distro-list () { @@ -34,12 +45,12 @@ while [[ $k1 -lt ${#MYARRAY1[@]} ]] if [[ $KERNEL_ONLY == "yes" ]]; then if [[ "$OLDFAMILY" != *"$LINUXFAMILY$BRANCH"* ]]; then echo "$BOARD $RELEASE $BRANCH $BUILD_DESKTOP $LINUXFAMILY" - source $SRC/lib/main.sh + [[ $BUILD_ALL != "demo" ]] && source $SRC/lib/main.sh OLDFAMILY=$OLDFAMILY"$LINUXFAMILY$BRANCH" fi else echo "$BOARD $RELEASE $BRANCH $BUILD_DESKTOP $LINUXFAMILY" - source $SRC/lib/main.sh + [[ $BUILD_ALL != "demo" ]] && source $SRC/lib/main.sh fi # kernel only k1=$[$k1+2] @@ -48,22 +59,22 @@ while [[ $k1 -lt ${#MYARRAY1[@]} ]] } IFS=";" -MYARRAY=($(cat $SRC/lib/configuration.sh | awk '/)#enabled/ || /#des/ || /#build/' | sed 's/)#enabled//g' | sed 's/#description //g' | sed 's/#build //g' | sed ':a;N;$!ba;s/\n/;/g')) + +MYARRAY=($(cat $SRC/lib/configuration.sh | awk '/)#enabled/ || /#des/ || /#build/' | sed -e 's/\t\t//' | sed 's/)#enabled//g' | sed 's/#description //g' | sed -e 's/\t//' | sed 's/#build //g' | sed ':a;N;$!ba;s/\n/;/g')) i1=$[0+$START] j1=$[1+$START] o1=$[2+$START] while [[ $i1 -lt ${#MYARRAY[@]} ]] do - if [ "${MYARRAY[$o1]}" == "1" ]; then + if [[ "${MYARRAY[$o1]}" == "1" || "${MYARRAY[$o1]}" == "3" || "${MYARRAY[$o1]}" == "6" ]]; then distro-list "${MYARRAY[$i1]}" "default" fi - if [ "${MYARRAY[$o1]}" == "2" ]; then + if [[ "${MYARRAY[$o1]}" == "2" || "${MYARRAY[$o1]}" == "3" || "${MYARRAY[$o1]}" == "5" || "${MYARRAY[$o1]}" == "6" ]]; then distro-list "${MYARRAY[$i1]}" "next" fi - if [ "${MYARRAY[$o1]}" == "3" ]; then - distro-list "${MYARRAY[$i1]}" "default" - distro-list "${MYARRAY[$i1]}" "next" + if [[ "${MYARRAY[$o1]}" == "4" || "${MYARRAY[$o1]}" == "5" || "${MYARRAY[$o1]}" == "6" ]]; then + distro-list "${MYARRAY[$i1]}" "dev" fi i1=$[$i1+3];j1=$[$j1+3];o1=$[$o1+3] diff --git a/config/boot-next.cmd b/config/boot-next.cmd deleted file mode 100644 index 4d876a7f3a..0000000000 --- a/config/boot-next.cmd +++ /dev/null @@ -1,5 +0,0 @@ -setenv bootargs console=tty1 root=/dev/mmcblk0p1 rootwait panic=10 consoleblank=0 -ext4load mmc 0 0x49000000 /boot/dtb/${fdtfile} -ext4load mmc 0 0x46000000 /boot/zImage -env set fdt_high ffffffff -bootz 0x46000000 - 0x49000000 diff --git a/configuration.sh b/configuration.sh index 04682e61f4..665fc5382b 100644 --- a/configuration.sh +++ b/configuration.sh @@ -54,7 +54,7 @@ cubieboard4)#enabled #description A80 octa core 2Gb soc wifi - #build 5 + #build 0 LINUXFAMILY="sun9i" BOOTCONFIG="Cubieboard4_defconfig" CPUMIN="1200000" @@ -73,7 +73,7 @@ cubieboard)#enabled #description A10 single core 1Gb SoC - #build 6 + #build 6 LINUXFAMILY="sun4i" BOOTCONFIG="Cubieboard_config" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sunxi" @@ -82,7 +82,7 @@ cubieboard2)#enabled #description A20 dual core 1Gb SoC - #build 6 + #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="Cubieboard2_config" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i" @@ -91,7 +91,7 @@ cubietruck)#enabled #description A20 dual core 2Gb SoC Wifi - #build 6 + #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="Cubietruck_config" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i ap6210" @@ -100,7 +100,7 @@ lime-a10)#enabled #description A10 single core 512Mb SoC - #build 6 + #build 6 LINUXFAMILY="sun4i" BOOTCONFIG="A10-OLinuXino-Lime_defconfig" MODULES="hci_uart gpio_sunxi rfcomm hidp bonding spi_sun7i 8021q a20_tp" @@ -118,7 +118,7 @@ lime2)#enabled #description A20 dual core 1Gb SoC - #build 6 + #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="A20-OLinuXino-Lime2_defconfig" MODULES="hci_uart gpio_sunxi rfcomm hidp bonding spi_sun7i 8021q a20_tp" @@ -126,7 +126,7 @@ ;; micro)#enabled - #description A20 dual core 1Gb SoC + #description A20 dual core 1Gb SoC #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="A20-OLinuXino_MICRO_config" @@ -136,7 +136,7 @@ pcduino3nano)#enabled #description A20 dual core 1Gb SoC - #build 6 + #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="Linksprite_pcDuino3_Nano_defconfig" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i" @@ -150,14 +150,14 @@ BOOTLOADER="https://github.com/BPI-SINOVOIP/BPI-Mainline-uboot" BOOTBRANCH="master" BOOTCONFIG="Bananapi_M2_defconfig" - BOOTSOURCE="u-boot-bpi-m2" + BOOTSOURCE="u-boot-bpi-m2" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i 8021q a20_tp" MODULES_NEXT="brcmfmac bonding" ;; bananapi)#enabled #description A20 dual core 1Gb SoC - #build 6 + #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="Bananapi_defconfig" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i 8021q a20_tp" @@ -166,7 +166,7 @@ bananapipro)#enabled #description A20 dual core 1Gb SoC Wifi - #build 0 + #build 0 LINUXFAMILY="sun7i" BOOTCONFIG="Bananapro_defconfig" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i 8021q a20_tp ap6210" @@ -175,7 +175,7 @@ lamobo-r1)#enabled #description A20 dual core 1Gb SoC Switch - #build 6 + #build 6 LINUXFAMILY="sun7i" BOOTCONFIG="Lamobo_R1_defconfig" MODULES="hci_uart gpio_sunxi rfcomm hidp sunxi-ir bonding spi_sun7i 8021q" @@ -203,7 +203,7 @@ orangepiplus)#enabled #description H3 quad core 1Gb SoC Wifi USB hub #build 4wip - LINUXFAMILY="sun8i" + LINUXFAMILY="sun8i" BOOTCONFIG="orangepi_plus_defconfig" LINUXKERNEL="https://github.com/jwrdegoede/linux-sunxi" LINUXSOURCE="hans" @@ -213,7 +213,7 @@ cubox-i)#enabled #description Freescale iMx dual/quad core Wifi #build 3 - LINUXFAMILY="cubox" + LINUXFAMILY="cubox" BOOTCONFIG="mx6_cubox-i_config" MODULES="bonding" MODULES_NEXT="bonding" @@ -226,13 +226,13 @@ LINUXFAMILY="udoo" BOOTCONFIG="udoo_qdl_config" MODULES="bonding" - MODULES_NEXT="" + MODULES_NEXT="" SERIALCON="ttymxc1" ;; udoo-neo)#enabled #description Freescale iMx singe core Wifi - #build 1wip + #build 1wip #BOOTSIZE="32" LINUXFAMILY="neo" BOOTCONFIG="udoo_neo_config" @@ -243,11 +243,11 @@ guitar)#enabled #description S500 Lemaker Guitar Action quad core - #build 1wip - LINUXFAMILY="s500" - OFFSET="16" + #build 1wip + LINUXFAMILY="s500" + OFFSET="16" BOOTSIZE="16" - BOOTCONFIG="s500_defconfig" + BOOTCONFIG="s500_defconfig" MODULES="ethernet wlan_8723bs bonding" MODULES_NEXT="" SERIALCON="ttyS3" @@ -255,10 +255,10 @@ odroidxu)#enabled #description Exynos5422 XU3/XU4 octa core - #build 3 - LINUXFAMILY="odroidxu" + #build 1 + LINUXFAMILY="odroidxu" BOOTSIZE="16" - BOOTCONFIG="odroid_config" + BOOTCONFIG="odroid_config" MODULES="bonding" MODULES_NEXT="" SERIALCON="ttySAC2" @@ -410,7 +410,7 @@ # For user override if [[ -f "$SRC/userpatches/lib.config" ]]; then - display_alert "Using user configuration override" "$SRC/userpatches/lib.config" "info" + #display_alert "Using user configuration override" "$SRC/userpatches/lib.config" "info" source $SRC/userpatches/lib.config fi diff --git a/patch/kernel/sunxi-default/debian_packaging-fix.patch b/patch/kernel/sunxi-default/debian_packaging-fix.patch deleted file mode 100644 index e528e0f2ba..0000000000 --- a/patch/kernel/sunxi-default/debian_packaging-fix.patch +++ /dev/null @@ -1,65 +0,0 @@ ---- a/scripts/package/builddeb -+++ b/scripts/package/builddeb -@@ -81,10 +81,10 @@ fwdir="$objtree/debian/fwtmp" - kernel_headers_dir="$objtree/debian/hdrtmp" - libc_headers_dir="$objtree/debian/headertmp" - dbg_dir="$objtree/debian/dbgtmp" --packagename=linux-image-$version --fwpackagename=linux-firmware-image-$version --kernel_headers_packagename=linux-headers-$version --libc_headers_packagename=linux-libc-dev -+packagename=linux-image"$LOCALVERSION" -+fwpackagename=linux-firmware-image"$LOCALVERSION" -+kernel_headers_packagename=linux-headers"$LOCALVERSION" -+libc_headers_packagename=linux-libc-dev"$LOCALVERSION" - dbg_packagename=$packagename-dbg - - if [ "$ARCH" = "um" ] ; then -@@ -177,7 +177,7 @@ fi - - if [ "$ARCH" != "um" ]; then - $MAKE headers_check KBUILD_SRC= -- $MAKE headers_install KBUILD_SRC= INSTALL_HDR_PATH="$libc_headers_dir/usr" -+ #$MAKE headers_install KBUILD_SRC= INSTALL_HDR_PATH="$libc_headers_dir/usr" - fi - - # Install the maintainer scripts -@@ -213,10 +213,25 @@ done - ## - ## Create sym link to kernel image - ## --kernel_tmp_version="${installed_image_path////\\/}" --sed -e "s/exit 0/ln -sf \/$kernel_tmp_version \/boot\/zImage/g" -i $tmpdir/DEBIAN/postinst --echo "rm -f /boot/.next" >> $tmpdir/DEBIAN/postinst --echo "exit 0" >> $tmpdir/DEBIAN/postinst -+sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/postinst -+cat >> $tmpdir/DEBIAN/postinst < /dev/null 2>&1 -+rm -f /$installed_image_path /boot/zImage -+else -+ln -sf /$installed_image_path /boot/zImage > /dev/null 2>&1 || mv /$installed_image_path /boot/zImage -+fi -+rm -f /boot/.next -+exit 0 -+EOT -+## -+## FAT install workaround -+## -+sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/preinst -+cat >> $tmpdir/DEBIAN/preinst <> $tmpdir/DEBIAN/preinst - - # Try to determine maintainer and email values - if [ -n "$DEBEMAIL" ]; then -@@ -359,7 +374,7 @@ EOF - - if [ "$ARCH" != "um" ]; then - create_package "$kernel_headers_packagename" "$kernel_headers_dir" -- create_package "$libc_headers_packagename" "$libc_headers_dir" -+ #create_package "$libc_headers_packagename" "$libc_headers_dir" - fi - - create_package "$packagename" "$tmpdir" diff --git a/patch/kernel/sunxi-default/fbtft_for_older.patch b/patch/kernel/sunxi-default/fbtft_for_older.patch deleted file mode 100644 index 55e63c1d9c..0000000000 --- a/patch/kernel/sunxi-default/fbtft_for_older.patch +++ /dev/null @@ -1,9505 +0,0 @@ -diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig -index df63338..b886bfb 100644 ---- a/drivers/video/Kconfig -+++ b/drivers/video/Kconfig -@@ -17,6 +17,8 @@ config SH_LCD_MIPI_DSI - - source "drivers/char/agp/Kconfig" - -+source "drivers/video/fbtft/Kconfig" -+ - source "drivers/gpu/vga/Kconfig" - - source "drivers/gpu/drm/Kconfig" -diff --git a/drivers/video/Makefile b/drivers/video/Makefile -index aacc4dc..dcaf309 100644 ---- a/drivers/video/Makefile -+++ b/drivers/video/Makefile -@@ -4,6 +4,7 @@ - - # Each configuration option enables a list of files. - -+obj-y += fbtft/ - obj-$(CONFIG_VGASTATE) += vgastate.o - obj-y += fb_notify.o - obj-$(CONFIG_FB) += fb.o -diff --git a/drivers/video/fbtft/Kconfig b/drivers/video/fbtft/Kconfig -new file mode 100644 -index 0000000..52d7b21 ---- /dev/null -+++ b/drivers/video/fbtft/Kconfig -@@ -0,0 +1,151 @@ -+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_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_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_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 -diff --git a/drivers/video/fbtft/Makefile b/drivers/video/fbtft/Makefile -new file mode 100644 -index 0000000..b0b24a1 ---- /dev/null -+++ b/drivers/video/fbtft/Makefile -@@ -0,0 +1,57 @@ -+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_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_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_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 -diff --git a/drivers/video/fbtft/README b/drivers/video/fbtft/README -new file mode 100644 -index 0000000..8198104 ---- /dev/null -+++ b/drivers/video/fbtft/README -@@ -0,0 +1,24 @@ -+ 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 -+ -+ 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/ -diff --git a/drivers/video/fbtft/fb_bd663474.c b/drivers/video/fbtft/fb_bd663474.c -new file mode 100644 -index 0000000..3506543 ---- /dev/null -+++ b/drivers/video/fbtft/fb_bd663474.c -@@ -0,0 +1,191 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); -+MODULE_AUTHOR("Seong-Woo Kim"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_hx8340bn.c b/drivers/video/fbtft/fb_hx8340bn.c -new file mode 100644 -index 0000000..ba5ead8 ---- /dev/null -+++ b/drivers/video/fbtft/fb_hx8340bn.c -@@ -0,0 +1,227 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_hx8347d.c b/drivers/video/fbtft/fb_hx8347d.c -new file mode 100644 -index 0000000..a71c12f ---- /dev/null -+++ b/drivers/video/fbtft/fb_hx8347d.c -@@ -0,0 +1,179 @@ -+/* -+ * 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 -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller"); -+MODULE_AUTHOR("Christian Vogelgsang"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_hx8353d.c b/drivers/video/fbtft/fb_hx8353d.c -new file mode 100644 -index 0000000..dae54e3 ---- /dev/null -+++ b/drivers/video/fbtft/fb_hx8353d.c -@@ -0,0 +1,164 @@ -+/* -+ * 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 -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller"); -+MODULE_AUTHOR("Petr Olivka"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ili9320.c b/drivers/video/fbtft/fb_ili9320.c -new file mode 100644 -index 0000000..48b7a13 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ili9320.c -@@ -0,0 +1,232 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ili9325.c b/drivers/video/fbtft/fb_ili9325.c -new file mode 100644 -index 0000000..9ef0677 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ili9325.c -@@ -0,0 +1,289 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ili9340.c b/drivers/video/fbtft/fb_ili9340.c -new file mode 100644 -index 0000000..46d6d54 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ili9340.c -@@ -0,0 +1,161 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ili9341.c b/drivers/video/fbtft/fb_ili9341.c -new file mode 100644 -index 0000000..13ece9a ---- /dev/null -+++ b/drivers/video/fbtft/fb_ili9341.c -@@ -0,0 +1,177 @@ -+/* -+ * 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 -+#include -+#include -+#include -+ -+#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<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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller"); -+MODULE_AUTHOR("Christian Vogelgsang"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ili9486.c b/drivers/video/fbtft/fb_ili9486.c -new file mode 100644 -index 0000000..88daf48 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ili9486.c -@@ -0,0 +1,119 @@ -+/* -+ * 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 -+#include -+#include -+ -+#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 = 16, -+ .width = WIDTH, -+ .height = HEIGHT, -+ .init_sequence = default_init_sequence, -+ .fbtftops = { -+ .set_addr_win = set_addr_win, -+ .set_var = set_var, -+ }, -+}; -+FBTFT_REGISTER_DRIVER(DRVNAME, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_pcd8544.c b/drivers/video/fbtft/fb_pcd8544.c -new file mode 100644 -index 0000000..de02148 ---- /dev/null -+++ b/drivers/video/fbtft/fb_pcd8544.c -@@ -0,0 +1,176 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ra8875.c b/drivers/video/fbtft/fb_ra8875.c -new file mode 100644 -index 0000000..fd18e3a ---- /dev/null -+++ b/drivers/video/fbtft/fb_ra8875.c -@@ -0,0 +1,329 @@ -+/****************************************************************************** -+ -+ ProjectName: FBTFT driver ***** ***** -+ for the RA8875 LCD Controller * * ************ -+ * ** ** * * -+ Copyright © 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 -+#include -+#include -+#include -+ -+#include -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller"); -+MODULE_AUTHOR("Pf@nne"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_s6d02a1.c b/drivers/video/fbtft/fb_s6d02a1.c -new file mode 100644 -index 0000000..497d293 ---- /dev/null -+++ b/drivers/video/fbtft/fb_s6d02a1.c -@@ -0,0 +1,166 @@ -+/* -+ * 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 -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller"); -+MODULE_AUTHOR("WOLFGANG BUENING"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_s6d1121.c b/drivers/video/fbtft/fb_s6d1121.c -new file mode 100644 -index 0000000..58d2c18 ---- /dev/null -+++ b/drivers/video/fbtft/fb_s6d1121.c -@@ -0,0 +1,206 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller"); -+MODULE_AUTHOR("Roman Rolinsky"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ssd1289.c b/drivers/video/fbtft/fb_ssd1289.c -new file mode 100644 -index 0000000..4180a66 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ssd1289.c -@@ -0,0 +1,204 @@ -+/* -+ * 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 -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ssd1306.c b/drivers/video/fbtft/fb_ssd1306.c -new file mode 100644 -index 0000000..2661f3e ---- /dev/null -+++ b/drivers/video/fbtft/fb_ssd1306.c -@@ -0,0 +1,227 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("SSD1306 OLED Driver"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ssd1331.c b/drivers/video/fbtft/fb_ssd1331.c -new file mode 100644 -index 0000000..ea09d87 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ssd1331.c -@@ -0,0 +1,203 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("SSD1331 OLED Driver"); -+MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_ssd1351.c b/drivers/video/fbtft/fb_ssd1351.c -new file mode 100644 -index 0000000..6cfc512 ---- /dev/null -+++ b/drivers/video/fbtft/fb_ssd1351.c -@@ -0,0 +1,256 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("SSD1351 OLED Driver"); -+MODULE_AUTHOR("James Davies"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_st7735r.c b/drivers/video/fbtft/fb_st7735r.c -new file mode 100644 -index 0000000..55972d2 ---- /dev/null -+++ b/drivers/video/fbtft/fb_st7735r.c -@@ -0,0 +1,193 @@ -+/* -+ * 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 -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_tinylcd.c b/drivers/video/fbtft/fb_tinylcd.c -new file mode 100644 -index 0000000..893f378 ---- /dev/null -+++ b/drivers/video/fbtft/fb_tinylcd.c -@@ -0,0 +1,123 @@ -+/* -+ * 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 -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+ -+MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_tls8204.c b/drivers/video/fbtft/fb_tls8204.c -new file mode 100644 -index 0000000..11aa63d ---- /dev/null -+++ b/drivers/video/fbtft/fb_tls8204.c -@@ -0,0 +1,175 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller"); -+MODULE_AUTHOR("Michael Hope"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_upd161704.c b/drivers/video/fbtft/fb_upd161704.c -new file mode 100644 -index 0000000..3ce6292 ---- /dev/null -+++ b/drivers/video/fbtft/fb_upd161704.c -@@ -0,0 +1,204 @@ -+/* -+ * 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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+MODULE_ALIAS("platform:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); -+MODULE_AUTHOR("Seong-Woo Kim"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fb_watterott.c b/drivers/video/fbtft/fb_watterott.c -new file mode 100644 -index 0000000..179ab6d ---- /dev/null -+++ b/drivers/video/fbtft/fb_watterott.c -@@ -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 -+#include -+#include -+#include -+#include -+ -+#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, &display); -+ -+MODULE_ALIAS("spi:" DRVNAME); -+ -+MODULE_DESCRIPTION("FB driver for the Watterott LCD Controller"); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fbtft-bus.c b/drivers/video/fbtft/fbtft-bus.c -new file mode 100644 -index 0000000..b3cddb0 ---- /dev/null -+++ b/drivers/video/fbtft/fbtft-bus.c -@@ -0,0 +1,256 @@ -+#include -+#include -+#include -+#include -+#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); -diff --git a/drivers/video/fbtft/fbtft-core.c b/drivers/video/fbtft/fbtft-core.c -new file mode 100644 -index 0000000..4018e41 ---- /dev/null -+++ b/drivers/video/fbtft/fbtft-core.c -@@ -0,0 +1,1320 @@ -+/* -+ * Copyright (C) 2013 Noralf Tronnes -+ * -+ * This driver is inspired by: -+ * st7735fb.c, Copyright (C) 2011, Matt Porter -+ * broadsheetfb.c, Copyright (C) 2008, Jaya Kumar -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "fbtft.h" -+ -+extern void fbtft_sysfs_init(struct fbtft_par *par); -+extern void fbtft_sysfs_exit(struct fbtft_par *par); -+extern void fbtft_expand_debug_value(unsigned long *debug); -+extern int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves, -+ const char *str, int size); -+ -+static unsigned long debug; -+module_param(debug, ulong , 0); -+MODULE_PARM_DESC(debug, "override device debug level"); -+ -+static bool dma = false; -+module_param(dma, bool, 0); -+MODULE_PARM_DESC(dma, "Use DMA buffer"); -+ -+ -+void fbtft_dbg_hex(const struct device *dev, int groupsize, -+ void *buf, size_t len, const char *fmt, ...) -+{ -+ va_list args; -+ static char textbuf[512]; -+ char *text = textbuf; -+ size_t text_len; -+ -+ va_start(args, fmt); -+ text_len = vscnprintf(text, sizeof(textbuf), fmt, args); -+ va_end(args); -+ -+ hex_dump_to_buffer(buf, len, 32, groupsize, text + text_len, -+ 512 - text_len, false); -+ -+ if (len > 32) -+ dev_info(dev, "%s ...\n", text); -+ else -+ dev_info(dev, "%s\n", text); -+} -+EXPORT_SYMBOL(fbtft_dbg_hex); -+ -+unsigned long fbtft_request_gpios_match(struct fbtft_par *par, -+ const struct fbtft_gpio *gpio) -+{ -+ int ret; -+ long val; -+ -+ fbtft_par_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, "%s('%s')\n", -+ __func__, gpio->name); -+ -+ if (strcasecmp(gpio->name, "reset") == 0) { -+ par->gpio.reset = gpio->gpio; -+ return GPIOF_OUT_INIT_HIGH; -+ } else if (strcasecmp(gpio->name, "dc") == 0) { -+ par->gpio.dc = gpio->gpio; -+ return GPIOF_OUT_INIT_LOW; -+ } else if (strcasecmp(gpio->name, "cs") == 0) { -+ par->gpio.cs = gpio->gpio; -+ return GPIOF_OUT_INIT_HIGH; -+ } else if (strcasecmp(gpio->name, "wr") == 0) { -+ par->gpio.wr = gpio->gpio; -+ return GPIOF_OUT_INIT_HIGH; -+ } else if (strcasecmp(gpio->name, "rd") == 0) { -+ par->gpio.rd = gpio->gpio; -+ return GPIOF_OUT_INIT_HIGH; -+ } else if (strcasecmp(gpio->name, "latch") == 0) { -+ par->gpio.latch = gpio->gpio; -+ return GPIOF_OUT_INIT_LOW; -+ } else if (gpio->name[0] == 'd' && gpio->name[1] == 'b') { -+ ret = kstrtol(&gpio->name[2], 10, &val); -+ if (ret == 0 && val < 16) { -+ par->gpio.db[val] = gpio->gpio; -+ return GPIOF_OUT_INIT_LOW; -+ } -+ } else if (strcasecmp(gpio->name, "led") == 0) { -+ par->gpio.led[0] = gpio->gpio; -+ return GPIOF_OUT_INIT_LOW; -+ } else if (strcasecmp(gpio->name, "led_") == 0) { -+ par->gpio.led[0] = gpio->gpio; -+ return GPIOF_OUT_INIT_HIGH; -+ } -+ -+ return FBTFT_GPIO_NO_MATCH; -+} -+ -+int fbtft_request_gpios(struct fbtft_par *par) -+{ -+ struct fbtft_platform_data *pdata = par->pdata; -+ const struct fbtft_gpio *gpio; -+ unsigned long flags; -+ int i; -+ int ret; -+ -+ /* Initialize gpios to disabled */ -+ par->gpio.reset = -1; -+ par->gpio.dc = -1; -+ par->gpio.rd = -1; -+ par->gpio.wr = -1; -+ par->gpio.cs = -1; -+ par->gpio.latch = -1; -+ for (i = 0; i < 16; i++) { -+ par->gpio.db[i] = -1; -+ par->gpio.led[i] = -1; -+ par->gpio.aux[i] = -1; -+ } -+ -+ if (pdata && pdata->gpios) { -+ gpio = pdata->gpios; -+ while (gpio->name[0]) { -+ flags = FBTFT_GPIO_NO_MATCH; -+ /* if driver provides match function, try it first, -+ if no match use our own */ -+ if (par->fbtftops.request_gpios_match) -+ flags = par->fbtftops.request_gpios_match(par, gpio); -+ if (flags == FBTFT_GPIO_NO_MATCH) -+ flags = fbtft_request_gpios_match(par, gpio); -+ if (flags != FBTFT_GPIO_NO_MATCH) { -+ ret = gpio_request_one(gpio->gpio, flags, -+ par->info->device->driver->name); -+ if (ret < 0) { -+ dev_err(par->info->device, -+ "%s: gpio_request_one('%s'=%d) failed with %d\n", -+ __func__, gpio->name, -+ gpio->gpio, ret); -+ return ret; -+ } -+ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, -+ "%s: '%s' = GPIO%d\n", -+ __func__, gpio->name, gpio->gpio); -+ } -+ gpio++; -+ } -+ } -+ -+ return 0; -+} -+ -+void fbtft_free_gpios(struct fbtft_par *par) -+{ -+ struct fbtft_platform_data *pdata = NULL; -+ const struct fbtft_gpio *gpio; -+ -+ fbtft_par_dbg(DEBUG_FREE_GPIOS, par, "%s()\n", __func__); -+ -+ if (par->spi) -+ pdata = par->spi->dev.platform_data; -+ if (par->pdev) -+ pdata = par->pdev->dev.platform_data; -+ -+ if (pdata && pdata->gpios) { -+ gpio = pdata->gpios; -+ while (gpio->name[0]) { -+ fbtft_par_dbg(DEBUG_FREE_GPIOS, par, -+ "%s(): gpio_free('%s'=%d)\n", -+ __func__, gpio->name, gpio->gpio); -+ /* if the gpio wasn't recognized by request_gpios, -+ WARN() will protest */ -+ gpio_direction_input(gpio->gpio); -+ gpio_free(gpio->gpio); -+ gpio++; -+ } -+ } -+} -+ -+#ifdef CONFIG_FB_BACKLIGHT -+int fbtft_backlight_update_status(struct backlight_device *bd) -+{ -+ struct fbtft_par *par = bl_get_data(bd); -+ bool polarity = !!(bd->props.state & BL_CORE_DRIVER1); -+ -+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, -+ "%s: polarity=%d, power=%d, fb_blank=%d\n", -+ __func__, polarity, bd->props.power, bd->props.fb_blank); -+ -+ if ((bd->props.power == FB_BLANK_UNBLANK) && (bd->props.fb_blank == FB_BLANK_UNBLANK)) -+ gpio_set_value(par->gpio.led[0], polarity); -+ else -+ gpio_set_value(par->gpio.led[0], !polarity); -+ -+ return 0; -+} -+ -+int fbtft_backlight_get_brightness(struct backlight_device *bd) -+{ -+ return bd->props.brightness; -+} -+ -+void fbtft_unregister_backlight(struct fbtft_par *par) -+{ -+ const struct backlight_ops *bl_ops; -+ -+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, "%s()\n", __func__); -+ -+ if (par->info->bl_dev) { -+ par->info->bl_dev->props.power = FB_BLANK_POWERDOWN; -+ backlight_update_status(par->info->bl_dev); -+ bl_ops = par->info->bl_dev->ops; -+ backlight_device_unregister(par->info->bl_dev); -+ par->info->bl_dev = NULL; -+ } -+} -+ -+void fbtft_register_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__); -+ -+ if (par->gpio.led[0] == -1) { -+ fbtft_par_dbg(DEBUG_BACKLIGHT, par, -+ "%s(): led pin not set, exiting.\n", __func__); -+ return; -+ } -+ -+ 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 memeory for backlight operations.\n", -+ __func__); -+ return; -+ } -+ -+ bl_ops->get_brightness = fbtft_backlight_get_brightness; -+ bl_ops->update_status = fbtft_backlight_update_status; -+ bl_props.type = BACKLIGHT_RAW; -+ /* Assume backlight is off, get polarity from current state of pin */ -+ bl_props.power = FB_BLANK_POWERDOWN; -+ if (!gpio_get_value(par->gpio.led[0])) -+ bl_props.state |= BL_CORE_DRIVER1; -+ -+ 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 -+void fbtft_register_backlight(struct fbtft_par *par) { }; -+void fbtft_unregister_backlight(struct fbtft_par *par) { }; -+#endif -+EXPORT_SYMBOL(fbtft_register_backlight); -+EXPORT_SYMBOL(fbtft_unregister_backlight); -+ -+void fbtft_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); -+} -+ -+ -+void fbtft_reset(struct fbtft_par *par) -+{ -+ if (par->gpio.reset == -1) -+ return; -+ fbtft_par_dbg(DEBUG_RESET, par, "%s()\n", __func__); -+ gpio_set_value(par->gpio.reset, 0); -+ udelay(20); -+ gpio_set_value(par->gpio.reset, 1); -+ mdelay(120); -+} -+ -+ -+void fbtft_update_display(struct fbtft_par *par, unsigned start_line, unsigned end_line) -+{ -+ size_t offset, len; -+ struct timespec ts_start, ts_end, ts_fps, ts_duration; -+ long fps_ms, fps_us, duration_ms, duration_us; -+ long fps, throughput; -+ bool timeit = false; -+ int ret = 0; -+ -+ if (unlikely(par->debug & (DEBUG_TIME_FIRST_UPDATE | DEBUG_TIME_EACH_UPDATE))) { -+ if ((par->debug & DEBUG_TIME_EACH_UPDATE) || \ -+ ((par->debug & DEBUG_TIME_FIRST_UPDATE) && !par->first_update_done)) { -+ getnstimeofday(&ts_start); -+ timeit = true; -+ } -+ } -+ -+ /* Sanity checks */ -+ if (start_line > end_line) { -+ dev_warn(par->info->device, -+ "%s: start_line=%u is larger than end_line=%u. Shouldn't happen, will do full display update\n", -+ __func__, start_line, end_line); -+ start_line = 0; -+ end_line = par->info->var.yres - 1; -+ } -+ if (start_line > par->info->var.yres - 1 || end_line > par->info->var.yres - 1) { -+ dev_warn(par->info->device, -+ "%s: start_line=%u or end_line=%u is larger than max=%d. Shouldn't happen, will do full display update\n", -+ __func__, start_line, end_line, par->info->var.yres - 1); -+ start_line = 0; -+ end_line = par->info->var.yres - 1; -+ } -+ -+ fbtft_par_dbg(DEBUG_UPDATE_DISPLAY, par, "%s(start_line=%u, end_line=%u)\n", -+ __func__, start_line, end_line); -+ -+ if (par->fbtftops.set_addr_win) -+ par->fbtftops.set_addr_win(par, 0, start_line, -+ par->info->var.xres-1, end_line); -+ -+ offset = start_line * par->info->fix.line_length; -+ len = (end_line - start_line + 1) * par->info->fix.line_length; -+ ret = par->fbtftops.write_vmem(par, offset, len); -+ if (ret < 0) -+ dev_err(par->info->device, -+ "%s: write_vmem failed to update display buffer\n", -+ __func__); -+ -+ if (unlikely(timeit)) { -+ getnstimeofday(&ts_end); -+ if (par->update_time.tv_nsec == 0 && par->update_time.tv_sec == 0) { -+ par->update_time.tv_sec = ts_start.tv_sec; -+ par->update_time.tv_nsec = ts_start.tv_nsec; -+ } -+ ts_fps = timespec_sub(ts_start, par->update_time); -+ par->update_time.tv_sec = ts_start.tv_sec; -+ par->update_time.tv_nsec = ts_start.tv_nsec; -+ fps_ms = (ts_fps.tv_sec * 1000) + ((ts_fps.tv_nsec / 1000000) % 1000); -+ fps_us = (ts_fps.tv_nsec / 1000) % 1000; -+ fps = fps_ms * 1000 + fps_us; -+ fps = fps ? 1000000 / fps : 0; -+ -+ ts_duration = timespec_sub(ts_end, ts_start); -+ duration_ms = (ts_duration.tv_sec * 1000) + ((ts_duration.tv_nsec / 1000000) % 1000); -+ duration_us = (ts_duration.tv_nsec / 1000) % 1000; -+ throughput = duration_ms * 1000 + duration_us; -+ throughput = throughput ? (len * 1000) / throughput : 0; -+ throughput = throughput * 1000 / 1024; -+ -+ dev_info(par->info->device, -+ "Display update: %ld kB/s (%ld.%.3ld ms), fps=%ld (%ld.%.3ld ms)\n", -+ throughput, duration_ms, duration_us, -+ fps, fps_ms, fps_us); -+ par->first_update_done = true; -+ } -+} -+ -+ -+void fbtft_mkdirty(struct fb_info *info, int y, int height) -+{ -+ struct fbtft_par *par = info->par; -+ struct fb_deferred_io *fbdefio = info->fbdefio; -+ -+ /* special case, needed ? */ -+ if (y == -1) { -+ y = 0; -+ height = info->var.yres - 1; -+ } -+ -+ /* Mark display lines/area as dirty */ -+ spin_lock(&par->dirty_lock); -+ if (y < par->dirty_lines_start) -+ par->dirty_lines_start = y; -+ if (y + height - 1 > par->dirty_lines_end) -+ par->dirty_lines_end = y + height - 1; -+ spin_unlock(&par->dirty_lock); -+ -+ /* Schedule deferred_io to update display (no-op if already on queue)*/ -+ schedule_delayed_work(&info->deferred_work, fbdefio->delay); -+} -+ -+void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist) -+{ -+ struct fbtft_par *par = info->par; -+ unsigned dirty_lines_start, dirty_lines_end; -+ struct page *page; -+ unsigned long index; -+ unsigned y_low = 0, y_high = 0; -+ int count = 0; -+ -+ spin_lock(&par->dirty_lock); -+ dirty_lines_start = par->dirty_lines_start; -+ dirty_lines_end = par->dirty_lines_end; -+ /* set display line markers as clean */ -+ par->dirty_lines_start = par->info->var.yres - 1; -+ par->dirty_lines_end = 0; -+ spin_unlock(&par->dirty_lock); -+ -+ /* Mark display lines as dirty */ -+ list_for_each_entry(page, pagelist, lru) { -+ count++; -+ index = page->index << PAGE_SHIFT; -+ y_low = index / info->fix.line_length; -+ y_high = (index + PAGE_SIZE - 1) / info->fix.line_length; -+ fbtft_dev_dbg(DEBUG_DEFERRED_IO, par, info->device, -+ "page->index=%lu y_low=%d y_high=%d\n", -+ page->index, y_low, y_high); -+ if (y_high > info->var.yres - 1) -+ y_high = info->var.yres - 1; -+ if (y_low < dirty_lines_start) -+ dirty_lines_start = y_low; -+ if (y_high > dirty_lines_end) -+ dirty_lines_end = y_high; -+ } -+ -+ par->fbtftops.update_display(info->par, -+ dirty_lines_start, dirty_lines_end); -+} -+ -+ -+void fbtft_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) -+{ -+ struct fbtft_par *par = info->par; -+ -+ fbtft_dev_dbg(DEBUG_FB_FILLRECT, par, info->dev, -+ "%s: dx=%d, dy=%d, width=%d, height=%d\n", -+ __func__, rect->dx, rect->dy, rect->width, rect->height); -+ sys_fillrect(info, rect); -+ -+ par->fbtftops.mkdirty(info, rect->dy, rect->height); -+} -+ -+void fbtft_fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) -+{ -+ struct fbtft_par *par = info->par; -+ -+ fbtft_dev_dbg(DEBUG_FB_COPYAREA, par, info->dev, -+ "%s: dx=%d, dy=%d, width=%d, height=%d\n", -+ __func__, area->dx, area->dy, area->width, area->height); -+ sys_copyarea(info, area); -+ -+ par->fbtftops.mkdirty(info, area->dy, area->height); -+} -+ -+void fbtft_fb_imageblit(struct fb_info *info, const struct fb_image *image) -+{ -+ struct fbtft_par *par = info->par; -+ -+ fbtft_dev_dbg(DEBUG_FB_IMAGEBLIT, par, info->dev, -+ "%s: dx=%d, dy=%d, width=%d, height=%d\n", -+ __func__, image->dx, image->dy, image->width, image->height); -+ sys_imageblit(info, image); -+ -+ par->fbtftops.mkdirty(info, image->dy, image->height); -+} -+ -+ssize_t fbtft_fb_write(struct fb_info *info, -+ const char __user *buf, size_t count, loff_t *ppos) -+{ -+ struct fbtft_par *par = info->par; -+ ssize_t res; -+ -+ fbtft_dev_dbg(DEBUG_FB_WRITE, par, info->dev, -+ "%s: count=%zd, ppos=%llu\n", __func__, count, *ppos); -+ res = fb_sys_write(info, buf, count, ppos); -+ -+ /* TODO: only mark changed area -+ update all for now */ -+ par->fbtftops.mkdirty(info, -1, 0); -+ -+ return res; -+} -+ -+/* from pxafb.c */ -+unsigned int chan_to_field(unsigned chan, struct fb_bitfield *bf) -+{ -+ chan &= 0xffff; -+ chan >>= 16 - bf->length; -+ return chan << bf->offset; -+} -+ -+int fbtft_fb_setcolreg(unsigned regno, -+ unsigned red, unsigned green, unsigned blue, -+ unsigned transp, struct fb_info *info) -+{ -+ struct fbtft_par *par = info->par; -+ unsigned val; -+ int ret = 1; -+ -+ fbtft_dev_dbg(DEBUG_FB_SETCOLREG, par, info->dev, -+ "%s(regno=%u, red=0x%X, green=0x%X, blue=0x%X, trans=0x%X)\n", -+ __func__, regno, red, green, blue, transp); -+ -+ switch (info->fix.visual) { -+ case FB_VISUAL_TRUECOLOR: -+ if (regno < 16) { -+ u32 *pal = info->pseudo_palette; -+ -+ val = chan_to_field(red, &info->var.red); -+ val |= chan_to_field(green, &info->var.green); -+ val |= chan_to_field(blue, &info->var.blue); -+ -+ pal[regno] = val; -+ ret = 0; -+ } -+ break; -+ -+ } -+ return ret; -+} -+ -+int fbtft_fb_blank(int blank, struct fb_info *info) -+{ -+ struct fbtft_par *par = info->par; -+ int ret = -EINVAL; -+ -+ fbtft_dev_dbg(DEBUG_FB_BLANK, par, info->dev, "%s(blank=%d)\n", -+ __func__, blank); -+ -+ if (!par->fbtftops.blank) -+ return ret; -+ -+ switch (blank) { -+ case FB_BLANK_POWERDOWN: -+ case FB_BLANK_VSYNC_SUSPEND: -+ case FB_BLANK_HSYNC_SUSPEND: -+ case FB_BLANK_NORMAL: -+ ret = par->fbtftops.blank(par, true); -+ break; -+ case FB_BLANK_UNBLANK: -+ ret = par->fbtftops.blank(par, false); -+ break; -+ } -+ return ret; -+} -+ -+void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src) -+{ -+ if (src->write) -+ dst->write = src->write; -+ if (src->read) -+ dst->read = src->read; -+ if (src->write_vmem) -+ dst->write_vmem = src->write_vmem; -+ if (src->write_register) -+ dst->write_register = src->write_register; -+ if (src->set_addr_win) -+ dst->set_addr_win = src->set_addr_win; -+ if (src->reset) -+ dst->reset = src->reset; -+ if (src->mkdirty) -+ dst->mkdirty = src->mkdirty; -+ if (src->update_display) -+ dst->update_display = src->update_display; -+ if (src->init_display) -+ dst->init_display = src->init_display; -+ if (src->blank) -+ dst->blank = src->blank; -+ if (src->request_gpios_match) -+ dst->request_gpios_match = src->request_gpios_match; -+ if (src->request_gpios) -+ dst->request_gpios = src->request_gpios; -+ if (src->free_gpios) -+ dst->free_gpios = src->free_gpios; -+ if (src->verify_gpios) -+ dst->verify_gpios = src->verify_gpios; -+ if (src->register_backlight) -+ dst->register_backlight = src->register_backlight; -+ if (src->unregister_backlight) -+ dst->unregister_backlight = src->unregister_backlight; -+ if (src->set_var) -+ dst->set_var = src->set_var; -+ if (src->set_gamma) -+ dst->set_gamma = src->set_gamma; -+} -+ -+/** -+ * fbtft_framebuffer_alloc - creates a new frame buffer info structure -+ * -+ * @display: pointer to structure describing the display -+ * @dev: pointer to the device for this fb, this can be NULL -+ * -+ * Creates a new frame buffer info structure. -+ * -+ * Also creates and populates the following structures: -+ * info->fbops -+ * info->fbdefio -+ * info->pseudo_palette -+ * par->fbtftops -+ * par->txbuf -+ * -+ * Returns the new structure, or NULL if an error occurred. -+ * -+ */ -+struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, -+ struct device *dev) -+{ -+ struct fb_info *info; -+ struct fbtft_par *par; -+ struct fb_ops *fbops = NULL; -+ struct fb_deferred_io *fbdefio = NULL; -+ struct fbtft_platform_data *pdata = dev->platform_data; -+ u8 *vmem = NULL; -+ void *txbuf = NULL; -+ void *buf = NULL; -+ unsigned width; -+ unsigned height; -+ int txbuflen = display->txbuflen; -+ unsigned bpp = display->bpp; -+ unsigned fps = display->fps; -+ unsigned rotate = 0; -+ bool bgr = false; -+ u8 startbyte = 0; -+ int vmem_size; -+ int *init_sequence = display->init_sequence; -+ char *gamma = display->gamma; -+ unsigned long *gamma_curves = NULL; -+ -+ /* sanity check */ -+ if (display->gamma_num * display->gamma_len > FBTFT_GAMMA_MAX_VALUES_TOTAL) { -+ dev_err(dev, -+ "%s: FBTFT_GAMMA_MAX_VALUES_TOTAL=%d is exceeded\n", -+ __func__, FBTFT_GAMMA_MAX_VALUES_TOTAL); -+ return NULL; -+ } -+ -+ /* defaults */ -+ if (!fps) -+ fps = 20; -+ if (!bpp) -+ bpp = 16; -+ -+ vmem_size = display->width*display->height*bpp/8; -+ -+ /* platform_data override ? */ -+ if (pdata) { -+ if (pdata->fps) -+ fps = pdata->fps; -+ if (pdata->txbuflen) -+ txbuflen = pdata->txbuflen; -+ rotate = pdata->rotate; -+ bgr = pdata->bgr; -+ startbyte = pdata->startbyte; -+ if (pdata->display.init_sequence) -+ init_sequence = pdata->display.init_sequence; -+ if (pdata->gamma) -+ gamma = pdata->gamma; -+ if (pdata->display.debug) -+ display->debug = pdata->display.debug; -+ if (pdata->display.backlight) -+ display->backlight = pdata->display.backlight; -+ } -+ -+ display->debug |= debug; -+ fbtft_expand_debug_value(&display->debug); -+ -+ switch (rotate) { -+ case 90: -+ case 270: -+ width = display->height; -+ height = display->width; -+ break; -+ default: -+ width = display->width; -+ height = display->height; -+ } -+ -+ vmem = vzalloc(vmem_size); -+ if (!vmem) -+ goto alloc_fail; -+ -+ fbops = devm_kzalloc(dev, sizeof(struct fb_ops), GFP_KERNEL); -+ if (!fbops) -+ goto alloc_fail; -+ -+ fbdefio = devm_kzalloc(dev, sizeof(struct fb_deferred_io), GFP_KERNEL); -+ if (!fbdefio) -+ goto alloc_fail; -+ -+ buf = devm_kzalloc(dev, 128, GFP_KERNEL); -+ if (!buf) -+ goto alloc_fail; -+ -+ if (display->gamma_num && display->gamma_len) { -+ gamma_curves = devm_kzalloc(dev, display->gamma_num * display->gamma_len * sizeof(gamma_curves[0]), -+ GFP_KERNEL); -+ if (!gamma_curves) -+ goto alloc_fail; -+ } -+ -+ info = framebuffer_alloc(sizeof(struct fbtft_par), dev); -+ if (!info) -+ goto alloc_fail; -+ -+ info->screen_base = (u8 __force __iomem *)vmem; -+ info->fbops = fbops; -+ info->fbdefio = fbdefio; -+ -+ fbops->owner = dev->driver->owner; -+ fbops->fb_read = fb_sys_read; -+ fbops->fb_write = fbtft_fb_write; -+ fbops->fb_fillrect = fbtft_fb_fillrect; -+ fbops->fb_copyarea = fbtft_fb_copyarea; -+ fbops->fb_imageblit = fbtft_fb_imageblit; -+ fbops->fb_setcolreg = fbtft_fb_setcolreg; -+ fbops->fb_blank = fbtft_fb_blank; -+ -+ fbdefio->delay = HZ/fps; -+ fbdefio->deferred_io = fbtft_deferred_io; -+ fb_deferred_io_init(info); -+ -+ strncpy(info->fix.id, dev->driver->name, 16); -+ info->fix.type = FB_TYPE_PACKED_PIXELS; -+ info->fix.visual = FB_VISUAL_TRUECOLOR; -+ info->fix.xpanstep = 0; -+ info->fix.ypanstep = 0; -+ info->fix.ywrapstep = 0; -+ info->fix.line_length = width*bpp/8; -+ info->fix.accel = FB_ACCEL_NONE; -+ info->fix.smem_len = vmem_size; -+ -+ info->var.rotate = rotate; -+ info->var.xres = width; -+ info->var.yres = height; -+ info->var.xres_virtual = info->var.xres; -+ info->var.yres_virtual = info->var.yres; -+ info->var.bits_per_pixel = bpp; -+ info->var.nonstd = 1; -+ -+ /* RGB565 */ -+ info->var.red.offset = 11; -+ info->var.red.length = 5; -+ info->var.green.offset = 5; -+ info->var.green.length = 6; -+ info->var.blue.offset = 0; -+ info->var.blue.length = 5; -+ info->var.transp.offset = 0; -+ info->var.transp.length = 0; -+ -+ info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; -+ -+ par = info->par; -+ par->info = info; -+ par->pdata = dev->platform_data; -+ par->debug = display->debug; -+ par->buf = buf; -+ spin_lock_init(&par->dirty_lock); -+ par->bgr = bgr; -+ par->startbyte = startbyte; -+ par->init_sequence = init_sequence; -+ par->gamma.curves = gamma_curves; -+ par->gamma.num_curves = display->gamma_num; -+ par->gamma.num_values = display->gamma_len; -+ mutex_init(&par->gamma.lock); -+ info->pseudo_palette = par->pseudo_palette; -+ -+ if (par->gamma.curves && gamma) -+ fbtft_gamma_parse_str(par, -+ par->gamma.curves, gamma, strlen(gamma)); -+ -+ /* Transmit buffer */ -+ if (txbuflen == -1) -+ txbuflen = vmem_size + 2; /* add in case startbyte is used */ -+ -+#ifdef __LITTLE_ENDIAN -+ if ((!txbuflen) && (bpp > 8)) -+ txbuflen = PAGE_SIZE; /* need buffer for byteswapping */ -+#endif -+ -+ if (txbuflen > 0) { -+ if (dma) { -+ dev->coherent_dma_mask = ~0; -+ txbuf = dmam_alloc_coherent(dev, txbuflen, &par->txbuf.dma, GFP_DMA); -+ } else { -+ txbuf = devm_kzalloc(par->info->device, txbuflen, GFP_KERNEL); -+ } -+ if (!txbuf) -+ goto alloc_fail; -+ par->txbuf.buf = txbuf; -+ par->txbuf.len = txbuflen; -+ } -+ -+ /* default fbtft operations */ -+ par->fbtftops.write = fbtft_write_spi; -+ par->fbtftops.read = fbtft_read_spi; -+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; -+ par->fbtftops.write_register = fbtft_write_reg8_bus8; -+ par->fbtftops.set_addr_win = fbtft_set_addr_win; -+ par->fbtftops.reset = fbtft_reset; -+ par->fbtftops.mkdirty = fbtft_mkdirty; -+ par->fbtftops.update_display = fbtft_update_display; -+ par->fbtftops.request_gpios = fbtft_request_gpios; -+ par->fbtftops.free_gpios = fbtft_free_gpios; -+ if (display->backlight) -+ par->fbtftops.register_backlight = fbtft_register_backlight; -+ -+ /* use driver provided functions */ -+ fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops); -+ -+ return info; -+ -+alloc_fail: -+ vfree(vmem); -+ -+ return NULL; -+} -+EXPORT_SYMBOL(fbtft_framebuffer_alloc); -+ -+/** -+ * fbtft_framebuffer_release - frees up all memory used by the framebuffer -+ * -+ * @info: frame buffer info structure -+ * -+ */ -+void fbtft_framebuffer_release(struct fb_info *info) -+{ -+ fb_deferred_io_cleanup(info); -+ vfree(info->screen_base); -+ framebuffer_release(info); -+} -+EXPORT_SYMBOL(fbtft_framebuffer_release); -+ -+/** -+ * fbtft_register_framebuffer - registers a tft frame buffer device -+ * @fb_info: frame buffer info structure -+ * -+ * Sets SPI driverdata if needed -+ * Requests needed gpios. -+ * Initializes display -+ * Updates display. -+ * Registers a frame buffer device @fb_info. -+ * -+ * Returns negative errno on error, or zero for success. -+ * -+ */ -+int fbtft_register_framebuffer(struct fb_info *fb_info) -+{ -+ int ret; -+ char text1[50] = ""; -+ char text2[50] = ""; -+ struct fbtft_par *par = fb_info->par; -+ struct spi_device *spi = par->spi; -+ -+ /* sanity checks */ -+ if (!par->fbtftops.init_display) { -+ dev_err(fb_info->device, "missing fbtftops.init_display()\n"); -+ return -EINVAL; -+ } -+ -+ if (spi) -+ spi_set_drvdata(spi, fb_info); -+ if (par->pdev) -+ platform_set_drvdata(par->pdev, fb_info); -+ -+ ret = par->fbtftops.request_gpios(par); -+ if (ret < 0) -+ goto reg_fail; -+ -+ if (par->fbtftops.verify_gpios) { -+ ret = par->fbtftops.verify_gpios(par); -+ if (ret < 0) -+ goto reg_fail; -+ } -+ -+ ret = par->fbtftops.init_display(par); -+ if (ret < 0) -+ goto reg_fail; -+ if (par->fbtftops.set_var) { -+ ret = par->fbtftops.set_var(par); -+ if (ret < 0) -+ goto reg_fail; -+ } -+ -+ /* update the entire display */ -+ par->fbtftops.update_display(par, 0, par->info->var.yres - 1); -+ -+ if (par->fbtftops.set_gamma && par->gamma.curves) { -+ ret = par->fbtftops.set_gamma(par, par->gamma.curves); -+ if (ret) -+ goto reg_fail; -+ } -+ -+ if (par->fbtftops.register_backlight) -+ par->fbtftops.register_backlight(par); -+ -+ ret = register_framebuffer(fb_info); -+ if (ret < 0) -+ goto reg_fail; -+ -+ fbtft_sysfs_init(par); -+ -+ if (par->txbuf.buf) -+ sprintf(text1, ", %d KiB %sbuffer memory", -+ par->txbuf.len >> 10, par->txbuf.dma ? "DMA " : ""); -+ if (spi) -+ sprintf(text2, ", spi%d.%d at %d MHz", spi->master->bus_num, -+ spi->chip_select, spi->max_speed_hz/1000000); -+ dev_info(fb_info->dev, -+ "%s frame buffer, %dx%d, %d KiB video memory%s, fps=%lu%s\n", -+ fb_info->fix.id, fb_info->var.xres, fb_info->var.yres, -+ fb_info->fix.smem_len >> 10, text1, -+ HZ/fb_info->fbdefio->delay, text2); -+ -+#ifdef CONFIG_FB_BACKLIGHT -+ /* Turn on backlight if available */ -+ if (fb_info->bl_dev) { -+ fb_info->bl_dev->props.power = FB_BLANK_UNBLANK; -+ fb_info->bl_dev->ops->update_status(fb_info->bl_dev); -+ } -+#endif -+ -+ return 0; -+ -+reg_fail: -+ if (par->fbtftops.unregister_backlight) -+ par->fbtftops.unregister_backlight(par); -+ if (spi) -+ spi_set_drvdata(spi, NULL); -+ if (par->pdev) -+ platform_set_drvdata(par->pdev, NULL); -+ par->fbtftops.free_gpios(par); -+ -+ return ret; -+} -+EXPORT_SYMBOL(fbtft_register_framebuffer); -+ -+/** -+ * fbtft_unregister_framebuffer - releases a tft frame buffer device -+ * @fb_info: frame buffer info structure -+ * -+ * Frees SPI driverdata if needed -+ * Frees gpios. -+ * Unregisters frame buffer device. -+ * -+ */ -+int fbtft_unregister_framebuffer(struct fb_info *fb_info) -+{ -+ struct fbtft_par *par = fb_info->par; -+ struct spi_device *spi = par->spi; -+ int ret; -+ -+ if (spi) -+ spi_set_drvdata(spi, NULL); -+ if (par->pdev) -+ platform_set_drvdata(par->pdev, NULL); -+ if (par->fbtftops.unregister_backlight) -+ par->fbtftops.unregister_backlight(par); -+ fbtft_sysfs_exit(par); -+ par->fbtftops.free_gpios(par); -+ ret = unregister_framebuffer(fb_info); -+ return ret; -+} -+EXPORT_SYMBOL(fbtft_unregister_framebuffer); -+ -+/** -+ * fbtft_init_display() - Generic init_display() function -+ * @par: Driver data -+ * -+ * Uses par->init_sequence to do the initialization -+ * -+ * Return: 0 if successful, negative if error -+ */ -+int fbtft_init_display(struct fbtft_par *par) -+{ -+ int buf[64]; -+ char msg[128]; -+ char str[16]; -+ int i = 0; -+ int j; -+ -+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); -+ -+ /* sanity check */ -+ if (!par->init_sequence) { -+ dev_err(par->info->device, -+ "error: init_sequence is not set\n"); -+ return -EINVAL; -+ } -+ -+ /* make sure stop marker exists */ -+ for (i = 0; i < FBTFT_MAX_INIT_SEQUENCE; i++) -+ if (par->init_sequence[i] == -3) -+ break; -+ if (i == FBTFT_MAX_INIT_SEQUENCE) { -+ dev_err(par->info->device, -+ "missing stop marker at end of init sequence\n"); -+ return -EINVAL; -+ } -+ -+ par->fbtftops.reset(par); -+ if (par->gpio.cs != -1) -+ gpio_set_value(par->gpio.cs, 0); /* Activate chip */ -+ -+ i = 0; -+ while (i < FBTFT_MAX_INIT_SEQUENCE) { -+ if (par->init_sequence[i] == -3) { -+ /* done */ -+ return 0; -+ } -+ if (par->init_sequence[i] >= 0) { -+ dev_err(par->info->device, -+ "missing delimiter at position %d\n", i); -+ return -EINVAL; -+ } -+ if (par->init_sequence[i+1] < 0) { -+ dev_err(par->info->device, -+ "missing value after delimiter %d at position %d\n", -+ par->init_sequence[i], i); -+ return -EINVAL; -+ } -+ switch (par->init_sequence[i]) { -+ case -1: -+ i++; -+ /* make debug message */ -+ strcpy(msg, ""); -+ j = i + 1; -+ while (par->init_sequence[j] >= 0) { -+ sprintf(str, "0x%02X ", par->init_sequence[j]); -+ strcat(msg, str); -+ j++; -+ } -+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, -+ "init: write(0x%02X) %s\n", -+ par->init_sequence[i], msg); -+ -+ /* Write */ -+ j = 0; -+ while (par->init_sequence[i] >= 0) { -+ if (j > 63) { -+ dev_err(par->info->device, -+ "%s: Maximum register values exceeded\n", -+ __func__); -+ return -EINVAL; -+ } -+ buf[j++] = par->init_sequence[i++]; -+ } -+ par->fbtftops.write_register(par, j, -+ buf[0], buf[1], buf[2], buf[3], -+ buf[4], buf[5], buf[6], buf[7], -+ buf[8], buf[9], buf[10], buf[11], -+ buf[12], buf[13], buf[14], buf[15], -+ buf[16], buf[17], buf[18], buf[19], -+ buf[20], buf[21], buf[22], buf[23], -+ buf[24], buf[25], buf[26], buf[27], -+ buf[28], buf[29], buf[30], buf[31], -+ buf[32], buf[33], buf[34], buf[35], -+ buf[36], buf[37], buf[38], buf[39], -+ buf[40], buf[41], buf[42], buf[43], -+ buf[44], buf[45], buf[46], buf[47], -+ buf[48], buf[49], buf[50], buf[51], -+ buf[52], buf[53], buf[54], buf[55], -+ buf[56], buf[57], buf[58], buf[59], -+ buf[60], buf[61], buf[62], buf[63]); -+ break; -+ case -2: -+ i++; -+ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, -+ "init: mdelay(%d)\n", par->init_sequence[i]); -+ mdelay(par->init_sequence[i++]); -+ break; -+ default: -+ dev_err(par->info->device, -+ "unknown delimiter %d at position %d\n", -+ par->init_sequence[i], i); -+ return -EINVAL; -+ } -+ } -+ -+ dev_err(par->info->device, -+ "%s: something is wrong. Shouldn't get here.\n", __func__); -+ return -EINVAL; -+} -+EXPORT_SYMBOL(fbtft_init_display); -+ -+/** -+ * fbtft_verify_gpios() - Generic verify_gpios() function -+ * @par: Driver data -+ * -+ * Uses @spi, @pdev and @buswidth to determine which GPIOs is needed -+ * -+ * Return: 0 if successful, negative if error -+ */ -+int fbtft_verify_gpios(struct fbtft_par *par) -+{ -+ struct fbtft_platform_data *pdata; -+ int i; -+ -+ fbtft_par_dbg(DEBUG_VERIFY_GPIOS, par, "%s()\n", __func__); -+ -+ pdata = par->info->device->platform_data; -+ if (!pdata) { -+ dev_warn(par->info->device, -+ "%s(): buswidth value is not available\n", __func__); -+ return 0; -+ } -+ -+ if (pdata->display.buswidth != 9 && par->startbyte == 0 && \ -+ par->gpio.dc < 0) { -+ dev_err(par->info->device, -+ "Missing info about 'dc' gpio. Aborting.\n"); -+ return -EINVAL; -+ } -+ -+ if (!par->pdev) -+ return 0; -+ -+ if (par->gpio.wr < 0) { -+ dev_err(par->info->device, "Missing 'wr' gpio. Aborting.\n"); -+ return -EINVAL; -+ } -+ for (i = 0; i < pdata->display.buswidth; i++) { -+ if (par->gpio.db[i] < 0) { -+ dev_err(par->info->device, -+ "Missing 'db%02d' gpio. Aborting.\n", i); -+ return -EINVAL; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * fbtft_probe_common() - Generic device probe() helper function -+ * @display: Display properties -+ * @sdev: SPI device -+ * @pdev: Platform device -+ * -+ * Allocates, initializes and registers a framebuffer -+ * -+ * Either @sdev or @pdev should be NULL -+ * -+ * Return: 0 if successful, negative if error -+ */ -+int fbtft_probe_common(struct fbtft_display *display, -+ struct spi_device *sdev, struct platform_device *pdev) -+{ -+ struct device *dev; -+ struct fb_info *info; -+ struct fbtft_par *par; -+ struct fbtft_platform_data *pdata; -+ int ret; -+ -+ if (sdev) -+ dev = &sdev->dev; -+ else -+ dev = &pdev->dev; -+ -+ if (unlikely(display->debug & DEBUG_DRIVER_INIT_FUNCTIONS)) -+ dev_info(dev, "%s()\n", __func__); -+ -+ pdata = dev->platform_data; -+ if (pdata) { -+ if (pdata->display.width) -+ display->width = pdata->display.width; -+ if (pdata->display.height) -+ display->height = pdata->display.height; -+ if (pdata->display.buswidth) -+ display->buswidth = pdata->display.buswidth; -+ } -+ -+ info = fbtft_framebuffer_alloc(display, dev); -+ if (!info) -+ return -ENOMEM; -+ -+ par = info->par; -+ if (sdev) -+ par->spi = sdev; -+ else -+ par->pdev = pdev; -+ -+ /* write register functions */ -+ if (display->regwidth == 8 && display->buswidth == 8) { -+ par->fbtftops.write_register = fbtft_write_reg8_bus8; -+ } else -+ if (display->regwidth == 8 && display->buswidth == 9 && par->spi) { -+ par->fbtftops.write_register = fbtft_write_reg8_bus9; -+ } else if (display->regwidth == 16 && display->buswidth == 8) { -+ par->fbtftops.write_register = fbtft_write_reg16_bus8; -+ } else if (display->regwidth == 16 && display->buswidth == 16) { -+ par->fbtftops.write_register = fbtft_write_reg16_bus16; -+ } else { -+ dev_warn(dev, -+ "no default functions for regwidth=%d and buswidth=%d\n", -+ display->regwidth, display->buswidth); -+ } -+ -+ /* write_vmem() functions */ -+ if (display->buswidth == 8) -+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus8; -+ else if (display->buswidth == 9) -+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus9; -+ else if (display->buswidth == 16) -+ par->fbtftops.write_vmem = fbtft_write_vmem16_bus16; -+ -+ /* GPIO write() functions */ -+ if (par->pdev) { -+ if (display->buswidth == 8) -+ par->fbtftops.write = fbtft_write_gpio8_wr; -+ else if (display->buswidth == 16) -+ par->fbtftops.write = fbtft_write_gpio16_wr; -+ } -+ -+ /* 9-bit SPI setup */ -+ if (par->spi && display->buswidth == 9) { -+ par->spi->bits_per_word = 9; -+ ret = par->spi->master->setup(par->spi); -+ if (ret) { -+ dev_warn(&par->spi->dev, -+ "9-bit SPI not available, emulating using 8-bit.\n"); -+ par->spi->bits_per_word = 8; -+ ret = par->spi->master->setup(par->spi); -+ 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; -+ } -+ } -+ -+ if (!par->fbtftops.verify_gpios) -+ par->fbtftops.verify_gpios = fbtft_verify_gpios; -+ -+ /* make sure we still use the driver provided functions */ -+ fbtft_merge_fbtftops(&par->fbtftops, &display->fbtftops); -+ -+ /* use init_sequence if provided */ -+ if (par->init_sequence) -+ par->fbtftops.init_display = fbtft_init_display; -+ -+ /* use platform_data provided functions above all */ -+ if (pdata) -+ fbtft_merge_fbtftops(&par->fbtftops, &pdata->display.fbtftops); -+ -+ ret = fbtft_register_framebuffer(info); -+ if (ret < 0) -+ goto out_release; -+ -+ return 0; -+ -+out_release: -+ fbtft_framebuffer_release(info); -+ -+ return ret; -+} -+EXPORT_SYMBOL(fbtft_probe_common); -+ -+/** -+ * fbtft_remove_common() - Generic device remove() helper function -+ * @dev: Device -+ * @info: Framebuffer -+ * -+ * Unregisters and releases the framebuffer -+ * -+ * Return: 0 if successful, negative if error -+ */ -+int fbtft_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; -+} -+EXPORT_SYMBOL(fbtft_remove_common); -+ -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/fbtft-io.c b/drivers/video/fbtft/fbtft-io.c -new file mode 100644 -index 0000000..dfa2c46 ---- /dev/null -+++ b/drivers/video/fbtft/fbtft-io.c -@@ -0,0 +1,409 @@ -+#include -+#include -+#include -+#include -+#ifdef CONFIG_ARCH_BCM2708 -+#include -+#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<gpio.wr), __io_address(GPIO_BASE+0x28)); -+ writel(0, __io_address(GPIO_BASE+0x28)); /* used as a delay */ -+ writel((1<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 */ -diff --git a/drivers/video/fbtft/fbtft-sysfs.c b/drivers/video/fbtft/fbtft-sysfs.c -new file mode 100644 -index 0000000..fb88232 ---- /dev/null -+++ b/drivers/video/fbtft/fbtft-sysfs.c -@@ -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, S_IRUGO | S_IWUGO, 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, S_IRUGO | S_IWUGO, 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]); -+} -diff --git a/drivers/video/fbtft/fbtft.h b/drivers/video/fbtft/fbtft.h -new file mode 100644 -index 0000000..2dffdb0 ---- /dev/null -+++ b/drivers/video/fbtft/fbtft.h -@@ -0,0 +1,435 @@ -+/* -+ * 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 -+#include -+#include -+#include -+ -+ -+#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 -+ -+/** -+ * 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); -+ void (*free_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, _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 struct spi_driver fbtft_driver_spi_driver = { \ -+ .driver = { \ -+ .name = _name, \ -+ .owner = THIS_MODULE, \ -+ }, \ -+ .probe = fbtft_driver_probe_spi, \ -+ .remove = fbtft_driver_remove_spi, \ -+}; \ -+ \ -+static struct platform_driver fbtft_driver_platform_driver = { \ -+ .driver = { \ -+ .name = _name, \ -+ .owner = THIS_MODULE, \ -+ }, \ -+ .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_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 */ -diff --git a/drivers/video/fbtft/fbtft_device.c b/drivers/video/fbtft/fbtft_device.c -new file mode 100644 -index 0000000..c3cbdc9 ---- /dev/null -+++ b/drivers/video/fbtft/fbtft_device.c -@@ -0,0 +1,1296 @@ -+/* -+ * -+ * 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 -+#include -+#include -+#include -+#include -+ -+#include "fbtft.h" -+ -+#define DRVNAME "fbtft_device" -+ -+#define MAX_GPIOS 32 -+ -+struct spi_device *spi_device; -+struct platform_device *p_device; -+ -+static char *name; -+module_param(name, charp, 0); -+MODULE_PARM_DESC(name, "Devicename (required). " \ -+"name=list => list all supported devices."); -+ -+static unsigned rotate; -+module_param(rotate, uint, 0); -+MODULE_PARM_DESC(rotate, -+"Angle to rotate display counter clockwise: 0, 90, 180, 270"); -+ -+static unsigned busnum; -+module_param(busnum, uint, 0); -+MODULE_PARM_DESC(busnum, "SPI bus number (default=0)"); -+ -+static unsigned cs; -+module_param(cs, uint, 0); -+MODULE_PARM_DESC(cs, "SPI chip select (default=0)"); -+ -+static unsigned speed; -+module_param(speed, uint, 0); -+MODULE_PARM_DESC(speed, "SPI speed (override device default)"); -+ -+static int mode = -1; -+module_param(mode, int, 0); -+MODULE_PARM_DESC(mode, "SPI mode (override device default)"); -+ -+static char *gpios; -+module_param(gpios, charp, 0); -+MODULE_PARM_DESC(gpios, -+"List of gpios. Comma separated with the form: reset:23,dc:24 " \ -+"(when overriding the default, all gpios must be specified)"); -+ -+static unsigned fps; -+module_param(fps, uint, 0); -+MODULE_PARM_DESC(fps, "Frames per second (override driver default)"); -+ -+static char *gamma; -+module_param(gamma, charp, 0); -+MODULE_PARM_DESC(gamma, -+"String representation of Gamma Curve(s). Driver specific."); -+ -+static int txbuflen; -+module_param(txbuflen, int, 0); -+MODULE_PARM_DESC(txbuflen, "txbuflen (override driver default)"); -+ -+static int bgr = -1; -+module_param(bgr, int, 0); -+MODULE_PARM_DESC(bgr, -+"BGR bit (supported by some drivers)."); -+ -+static unsigned startbyte; -+module_param(startbyte, uint, 0); -+MODULE_PARM_DESC(startbyte, "Sets the Start byte used by some SPI displays."); -+ -+static bool custom; -+module_param(custom, bool, 0); -+MODULE_PARM_DESC(custom, "Add a custom display device. " \ -+"Use speed= argument to make it a SPI device, else platform_device"); -+ -+static unsigned width; -+module_param(width, uint, 0); -+MODULE_PARM_DESC(width, "Display width, used with the custom argument"); -+ -+static unsigned height; -+module_param(height, uint, 0); -+MODULE_PARM_DESC(height, "Display height, used with the custom argument"); -+ -+static unsigned buswidth = 8; -+module_param(buswidth, uint, 0); -+MODULE_PARM_DESC(buswidth, "Display bus width, used with the custom argument"); -+ -+static int init[FBTFT_MAX_INIT_SEQUENCE]; -+static int init_num; -+module_param_array(init, int, &init_num, 0); -+MODULE_PARM_DESC(init, "Init sequence, used with the custom argument"); -+ -+static unsigned long debug; -+module_param(debug, ulong , 0); -+MODULE_PARM_DESC(debug, -+"level: 0-7 (the remaining 29 bits is for advanced usage)"); -+ -+static unsigned verbose = 3; -+module_param(verbose, uint, 0); -+MODULE_PARM_DESC(verbose, -+"0 silent, >0 show gpios, >1 show devices, >2 show devices before (default=3)"); -+ -+ -+struct fbtft_device_display { -+ char *name; -+ struct spi_board_info *spi; -+ struct platform_device *pdev; -+}; -+ -+static void fbtft_device_pdev_release(struct device *dev); -+ -+static int write_gpio16_wr_slow(struct fbtft_par *par, void *buf, size_t len); -+static void adafruit18_green_tab_set_addr_win(struct fbtft_par *par, -+ int xs, int ys, int xe, int ye); -+ -+#define ADAFRUIT18_GAMMA \ -+ "02 1c 07 12 37 32 29 2d 29 25 2B 39 00 01 03 10\n" \ -+ "03 1d 07 06 2E 2C 29 2D 2E 2E 37 3F 00 00 02 10" -+ -+static int hy28b_init_sequence[] = { -+ -1,0x00e7,0x0010,-1,0x0000,0x0001,-1,0x0001,0x0100,-1,0x0002,0x0700, -+ -1,0x0003,0x1030,-1,0x0004,0x0000,-1,0x0008,0x0207,-1,0x0009,0x0000, -+ -1,0x000a,0x0000,-1,0x000c,0x0001,-1,0x000d,0x0000,-1,0x000f,0x0000, -+ -1,0x0010,0x0000,-1,0x0011,0x0007,-1,0x0012,0x0000,-1,0x0013,0x0000, -+ -2,50,-1,0x0010,0x1590,-1,0x0011,0x0227,-2,50,-1,0x0012,0x009c,-2,50, -+ -1,0x0013,0x1900,-1,0x0029,0x0023,-1,0x002b,0x000e,-2,50, -+ -1,0x0020,0x0000,-1,0x0021,0x0000,-2,50,-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,0x0000,-1,0x0093,0x0003,-1,0x0095,0x0110, -+ -1,0x0097,0x0000,-1,0x0098,0x0000,-1,0x0007,0x0133,-1,0x0020,0x0000, -+ -1,0x0021,0x0000,-2,100,-3 }; -+ -+#define HY28B_GAMMA \ -+ "04 1F 4 7 7 0 7 7 6 0\n" \ -+ "0F 00 1 7 4 0 0 0 6 7" -+ -+static int pitft_init_sequence[] = { -+ -1,0x01,-2,5,-1,0x28,-1,0xEF,0x03,0x80,0x02,-1,0xCF,0x00,0xC1,0x30, -+ -1,0xED,0x64,0x03,0x12,0x81,-1,0xE8,0x85,0x00,0x78, -+ -1,0xCB,0x39,0x2C,0x00,0x34,0x02,-1,0xF7,0x20,-1,0xEA,0x00,0x00, -+ -1,0xC0,0x23,-1,0xC1,0x10,-1,0xC5,0x3e,0x28,-1,0xC7,0x86,-1,0x3A,0x55, -+ -1,0xB1,0x00,0x18,-1,0xB6,0x08,0x82,0x27,-1,0xF2,0x00,-1,0x26,0x01, -+ -1,0xE0,0x0F,0x31,0x2B,0x0C,0x0E,0x08,0x4E,0xF1,0x37,0x07,0x10,0x03, -+ 0x0E,0x09,0x00,-1,0xE1,0x00,0x0E,0x14,0x03,0x11,0x07,0x31,0xC1,0x48, -+ 0x08,0x0F,0x0C,0x31,0x36,0x0F,-1,0x11,-2,100,-1,0x29,-2,20,-3 }; -+ -+/* Supported displays in alphabetical order */ -+static struct fbtft_device_display displays[] = { -+ { -+ .name = "adafruit18", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_st7735r", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ .gamma = ADAFRUIT18_GAMMA, -+ } -+ } -+ }, { -+ .name = "adafruit18_green", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_st7735r", -+ .max_speed_hz = 4000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ .fbtftops.set_addr_win = \ -+ adafruit18_green_tab_set_addr_win, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ .gamma = ADAFRUIT18_GAMMA, -+ } -+ } -+ }, { -+ .name = "adafruit22", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_hx8340bn", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 9, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "led", 23 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "adafruit22a", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9340", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "adafruit13m", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ssd1306", -+ .max_speed_hz = 16000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "er_tftm050_2", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ra8875", -+ .max_speed_hz = 5000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ .width = 480, -+ .height = 272, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "er_tftm070_5", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ra8875", -+ .max_speed_hz = 5000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ .width = 800, -+ .height = 480, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "flexfb", -+ .spi = &(struct spi_board_info) { -+ .modalias = "flexfb", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "flexpfb", -+ .pdev = &(struct platform_device) { -+ .name = "flexpfb", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 17 }, -+ { "dc", 1 }, -+ { "wr", 0 }, -+ { "cs", 21 }, -+ { "db00", 9 }, -+ { "db01", 11 }, -+ { "db02", 18 }, -+ { "db03", 23 }, -+ { "db04", 24 }, -+ { "db05", 25 }, -+ { "db06", 8 }, -+ { "db07", 7 }, -+ { "led", 4 }, -+ {}, -+ }, -+ }, -+ } -+ } -+ }, { -+ .name = "freetronicsoled128", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ssd1351", -+ .max_speed_hz = 20000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = FBTFT_ONBOARD_BACKLIGHT, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 24 }, -+ { "dc", 25 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "hx8353d", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_hx8353d", -+ .max_speed_hz = 16000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 23 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "hy28a", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9320", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .startbyte = 0b01110000, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "hy28b", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9325", -+ .max_speed_hz = 48000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ .init_sequence = hy28b_init_sequence, -+ }, -+ .startbyte = 0b01110000, -+ .bgr = true, -+ .fps= 50, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "led", 18 }, -+ {}, -+ }, -+ .gamma = HY28B_GAMMA, -+ } -+ } -+ }, { -+ .name = "itdb24", -+ .pdev = &(struct platform_device) { -+ .name = "fb_s6d1121", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = false, -+ .gpios = (const struct fbtft_gpio []) { -+ /* Wiring for LCD adapter kit */ -+ { "reset", 7 }, -+ { "dc", 0 }, /* rev 2: 2 */ -+ { "wr", 1 }, /* rev 2: 3 */ -+ { "cs", 8 }, -+ { "db00", 17 }, -+ { "db01", 18 }, -+ { "db02", 21 }, /* rev 2: 27 */ -+ { "db03", 22 }, -+ { "db04", 23 }, -+ { "db05", 24 }, -+ { "db06", 25 }, -+ { "db07", 4 }, -+ {} -+ }, -+ }, -+ } -+ } -+ }, { -+ .name = "itdb28", -+ .pdev = &(struct platform_device) { -+ .name = "fb_ili9325", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ }, -+ } -+ } -+ }, { -+ .name = "itdb28_spi", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9325", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "mi0283qt-2", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_hx8347d", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .startbyte = 0b01110000, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "mi0283qt-9a", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9341", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 9, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "mi0283qt-v2", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_watterott", -+ .max_speed_hz = 4000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "nokia3310", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_pcd8544", -+ .max_speed_hz = 400000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 23 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "nokia3310a", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_tls8204", -+ .max_speed_hz = 1000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 23 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "piscreen", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9486", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 22 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "pitft", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9340", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .chip_select = 0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ .init_sequence = pitft_init_sequence, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "dc", 25 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "pioled", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ssd1351", -+ .max_speed_hz = 20000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 24 }, -+ { "dc", 25 }, -+ {}, -+ }, -+ .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 3 " \ -+ "3 3 3 3 3 3 3 3 " \ -+ "3 3 3 3 3 3 3 3 " \ -+ "3 3 3 4 4 4 4 4 " \ -+ "4 4 4 4 4 4 4" -+ } -+ } -+ }, { -+ .name = "rpi-display", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9341", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 23 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "s6d02a1", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_s6d02a1", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 23 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "sainsmart18", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_st7735r", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "sainsmart32", -+ .pdev = &(struct platform_device) { -+ .name = "fb_ssd1289", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 16, -+ .txbuflen = -2, /* disable buffer */ -+ .backlight = 1, -+ .fbtftops.write = write_gpio16_wr_slow, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ }, -+ }, -+ } -+ }, { -+ .name = "sainsmart32_fast", -+ .pdev = &(struct platform_device) { -+ .name = "fb_ssd1289", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 16, -+ .txbuflen = -2, /* disable buffer */ -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ }, -+ }, -+ } -+ }, { -+ .name = "sainsmart32_latched", -+ .pdev = &(struct platform_device) { -+ .name = "fb_ssd1289", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 16, -+ .txbuflen = -2, /* disable buffer */ -+ .backlight = 1, -+ .fbtftops.write = \ -+ fbtft_write_gpio16_wr_latched, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ }, -+ }, -+ } -+ }, { -+ .name = "sainsmart32_spi", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ssd1289", -+ .max_speed_hz = 16000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "spidev", -+ .spi = &(struct spi_board_info) { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 0, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "ssd1331", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ssd1331", -+ .max_speed_hz = 20000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 24 }, -+ { "dc", 25 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "tinylcd35", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_tinylcd", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "tm022hdh26", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_ili9341", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ .backlight = 1, -+ }, -+ .bgr = true, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 25 }, -+ { "dc", 24 }, -+ { "led", 18 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "upd161704", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_upd161704", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 24 }, -+ { "dc", 25 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ .name = "waveshare22", -+ .spi = &(struct spi_board_info) { -+ .modalias = "fb_bd663474", -+ .max_speed_hz = 32000000, -+ .mode = SPI_MODE_3, -+ .platform_data = &(struct fbtft_platform_data) { -+ .display = { -+ .buswidth = 8, -+ }, -+ .gpios = (const struct fbtft_gpio []) { -+ { "reset", 24 }, -+ { "dc", 25 }, -+ {}, -+ }, -+ } -+ } -+ }, { -+ /* This should be the last item. -+ Used with the custom argument */ -+ .name = "", -+ .spi = &(struct spi_board_info) { -+ .modalias = "", -+ .max_speed_hz = 0, -+ .mode = SPI_MODE_0, -+ .platform_data = &(struct fbtft_platform_data) { -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ } -+ }, -+ .pdev = &(struct platform_device) { -+ .name = "", -+ .id = 0, -+ .dev = { -+ .release = fbtft_device_pdev_release, -+ .platform_data = &(struct fbtft_platform_data) { -+ .gpios = (const struct fbtft_gpio []) { -+ {}, -+ }, -+ }, -+ }, -+ }, -+ } -+}; -+ -+static int write_gpio16_wr_slow(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; -+} -+ -+static void adafruit18_green_tab_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, 0x2A, 0, xs + 2, 0, xe + 2); -+ write_reg(par, 0x2B, 0, ys + 1, 0, ye + 1); -+ write_reg(par, 0x2C); -+} -+ -+/* used if gpios parameter is present */ -+static struct fbtft_gpio fbtft_device_param_gpios[MAX_GPIOS+1] = { }; -+ -+static void fbtft_device_pdev_release(struct device *dev) -+{ -+/* Needed to silence this message: -+Device 'xxx' does not have a release() function, it is broken and must be fixed -+*/ -+} -+ -+static int spi_device_found(struct device *dev, void *data) -+{ -+ struct spi_device *spi = container_of(dev, struct spi_device, dev); -+ -+ pr_info(DRVNAME": %s %s %dkHz %d bits mode=0x%02X\n", -+ spi->modalias, dev_name(dev), spi->max_speed_hz/1000, -+ spi->bits_per_word, spi->mode); -+ -+ return 0; -+} -+ -+static void pr_spi_devices(void) -+{ -+ pr_info(DRVNAME": SPI devices registered:\n"); -+ bus_for_each_dev(&spi_bus_type, NULL, NULL, spi_device_found); -+} -+ -+static int p_device_found(struct device *dev, void *data) -+{ -+ struct platform_device -+ *pdev = container_of(dev, struct platform_device, dev); -+ -+ if (strstr(pdev->name, "fb")) -+ pr_info(DRVNAME": %s id=%d pdata? %s\n", -+ pdev->name, pdev->id, -+ pdev->dev.platform_data ? "yes" : "no"); -+ -+ return 0; -+} -+ -+static void pr_p_devices(void) -+{ -+ pr_info(DRVNAME": 'fb' Platform devices registered:\n"); -+ bus_for_each_dev(&platform_bus_type, NULL, NULL, p_device_found); -+} -+ -+#ifdef MODULE -+static void fbtft_device_spi_delete(struct spi_master *master, unsigned cs) -+{ -+ struct device *dev; -+ char str[32]; -+ -+ snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs); -+ -+ dev = bus_find_device_by_name(&spi_bus_type, NULL, str); -+ if (dev) { -+ if (verbose) -+ pr_info(DRVNAME": Deleting %s\n", str); -+ device_del(dev); -+ } -+} -+ -+static int fbtft_device_spi_device_register(struct spi_board_info *spi) -+{ -+ struct spi_master *master; -+ -+ master = spi_busnum_to_master(spi->bus_num); -+ if (!master) { -+ pr_err(DRVNAME ": spi_busnum_to_master(%d) returned NULL\n", -+ spi->bus_num); -+ return -EINVAL; -+ } -+ /* make sure it's available */ -+ fbtft_device_spi_delete(master, spi->chip_select); -+ spi_device = spi_new_device(master, spi); -+ put_device(&master->dev); -+ if (!spi_device) { -+ pr_err(DRVNAME ": spi_new_device() returned NULL\n"); -+ return -EPERM; -+ } -+ return 0; -+} -+#else -+static int fbtft_device_spi_device_register(struct spi_board_info *spi) -+{ -+ return spi_register_board_info(spi, 1); -+} -+#endif -+ -+static int __init fbtft_device_init(void) -+{ -+ struct spi_board_info *spi = NULL; -+ struct fbtft_platform_data *pdata; -+ const struct fbtft_gpio *gpio = NULL; -+ char *p_gpio, *p_name, *p_num; -+ bool found = false; -+ int i = 0; -+ long val; -+ int ret = 0; -+ -+ pr_debug("\n\n"DRVNAME": init\n"); -+ -+ if (name == NULL) { -+#ifdef MODULE -+ pr_err(DRVNAME": missing module parameter: 'name'\n"); -+ return -EINVAL; -+#else -+ return 0; -+#endif -+ } -+ -+ if (init_num > FBTFT_MAX_INIT_SEQUENCE) { -+ pr_err(DRVNAME \ -+ ": init parameter: exceeded max array size: %d\n", -+ FBTFT_MAX_INIT_SEQUENCE); -+ return -EINVAL; -+ } -+ -+ /* parse module parameter: gpios */ -+ while ((p_gpio = strsep(&gpios, ","))) { -+ if (strchr(p_gpio, ':') == NULL) { -+ pr_err(DRVNAME \ -+ ": error: missing ':' in gpios parameter: %s\n", -+ p_gpio); -+ return -EINVAL; -+ } -+ p_num = p_gpio; -+ p_name = strsep(&p_num, ":"); -+ if (p_name == NULL || p_num == NULL) { -+ pr_err(DRVNAME \ -+ ": something bad happened parsing gpios parameter: %s\n", -+ p_gpio); -+ return -EINVAL; -+ } -+ ret = kstrtol(p_num, 10, &val); -+ if (ret) { -+ pr_err(DRVNAME \ -+ ": could not parse number in gpios parameter: %s:%s\n", -+ p_name, p_num); -+ return -EINVAL; -+ } -+ strcpy(fbtft_device_param_gpios[i].name, p_name); -+ fbtft_device_param_gpios[i++].gpio = (int) val; -+ if (i == MAX_GPIOS) { -+ pr_err(DRVNAME \ -+ ": gpios parameter: exceeded max array size: %d\n", -+ MAX_GPIOS); -+ return -EINVAL; -+ } -+ } -+ if (fbtft_device_param_gpios[0].name[0]) -+ gpio = fbtft_device_param_gpios; -+ -+ if (verbose > 2) -+ pr_spi_devices(); /* print list of registered SPI devices */ -+ -+ if (verbose > 2) -+ pr_p_devices(); /* print list of 'fb' platform devices */ -+ -+ pr_debug(DRVNAME": name='%s', busnum=%d, cs=%d\n", name, busnum, cs); -+ -+ if (rotate > 0 && rotate < 4) { -+ rotate = (4 - rotate) * 90; -+ pr_warn("argument 'rotate' should be an angle. Values 1-3 is deprecated. Setting it to %d.\n", -+ rotate); -+ } -+ if (rotate != 0 && rotate != 90 && rotate != 180 && rotate != 270) { -+ pr_warn("argument 'rotate' illegal value: %d. Setting it to 0.\n", -+ rotate); -+ rotate = 0; -+ } -+ -+ /* name=list lists all supported displays */ -+ if (strncmp(name, "list", 32) == 0) { -+ pr_info(DRVNAME": Supported displays:\n"); -+ -+ for (i = 0; i < ARRAY_SIZE(displays); i++) -+ pr_info(DRVNAME": %s\n", displays[i].name); -+ return -ECANCELED; -+ } -+ -+ if (custom) { -+ i = ARRAY_SIZE(displays) - 1; -+ displays[i].name = name; -+ if (speed == 0) { -+ displays[i].pdev->name = name; -+ displays[i].spi = NULL; -+ } else { -+ strncpy(displays[i].spi->modalias, name, SPI_NAME_SIZE); -+ displays[i].pdev = NULL; -+ } -+ } -+ -+ for (i = 0; i < ARRAY_SIZE(displays); i++) { -+ if (strncmp(name, displays[i].name, 32) == 0) { -+ if (displays[i].spi) { -+ spi = displays[i].spi; -+ spi->chip_select = cs; -+ spi->bus_num = busnum; -+ if (speed) -+ spi->max_speed_hz = speed; -+ if (mode != -1) -+ spi->mode = mode; -+ pdata = (void *)spi->platform_data; -+ } else if (displays[i].pdev) { -+ p_device = displays[i].pdev; -+ pdata = p_device->dev.platform_data; -+ } else { -+ pr_err(DRVNAME": broken displays array\n"); -+ return -EINVAL; -+ } -+ -+ pdata->rotate = rotate; -+ if (bgr == 0) -+ pdata->bgr = false; -+ else if (bgr == 1) -+ pdata->bgr = true; -+ if (startbyte) -+ pdata->startbyte = startbyte; -+ if (gamma) -+ pdata->gamma = gamma; -+ pdata->display.debug = debug; -+ if (fps) -+ pdata->fps = fps; -+ if (txbuflen) -+ pdata->txbuflen = txbuflen; -+ if (init_num) -+ pdata->display.init_sequence = init; -+ if (gpio) -+ pdata->gpios = gpio; -+ if (custom) { -+ pdata->display.width = width; -+ pdata->display.height = height; -+ pdata->display.buswidth = buswidth; -+ pdata->display.backlight = 1; -+ } -+ -+ if (displays[i].spi) { -+ ret = fbtft_device_spi_device_register(spi); -+ if (ret) { -+ pr_err(DRVNAME \ -+ ": failed to register SPI device\n"); -+ return ret; -+ } -+ found = true; -+ break; -+ } else { -+ ret = platform_device_register(p_device); -+ if (ret < 0) { -+ pr_err(DRVNAME \ -+ ": platform_device_register() returned %d\n", -+ ret); -+ return ret; -+ } -+ found = true; -+ break; -+ } -+ } -+ } -+ -+ if (!found) { -+ pr_err(DRVNAME": display not supported: '%s'\n", name); -+ return -EINVAL; -+ } -+ -+ if (verbose && pdata && pdata->gpios) { -+ gpio = pdata->gpios; -+ pr_info(DRVNAME": GPIOS used by '%s':\n", name); -+ found = false; -+ while (verbose && gpio->name[0]) { -+ pr_info(DRVNAME": '%s' = GPIO%d\n", -+ gpio->name, gpio->gpio); -+ gpio++; -+ found = true; -+ } -+ if (!found) -+ pr_info(DRVNAME": (none)\n"); -+ } -+ -+ if (spi_device && (verbose > 1)) -+ pr_spi_devices(); -+ if (p_device && (verbose > 1)) -+ pr_p_devices(); -+ -+ return 0; -+} -+ -+static void __exit fbtft_device_exit(void) -+{ -+ pr_debug(DRVNAME" - exit\n"); -+ -+ if (spi_device) { -+ device_del(&spi_device->dev); -+ kfree(spi_device); -+ } -+ -+ if (p_device) -+ platform_device_unregister(p_device); -+ -+} -+ -+arch_initcall(fbtft_device_init); -+module_exit(fbtft_device_exit); -+ -+MODULE_DESCRIPTION("Add a FBTFT device."); -+MODULE_AUTHOR("Noralf Tronnes"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbtft/flexfb.c b/drivers/video/fbtft/flexfb.c -new file mode 100644 -index 0000000..45574a0 ---- /dev/null -+++ b/drivers/video/fbtft/flexfb.c -@@ -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 -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#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"); diff --git a/patch/kernel/sunxi-default/headers-packing.patch b/patch/kernel/sunxi-default/headers-packing.patch deleted file mode 100644 index 1cf5b59ec9..0000000000 --- a/patch/kernel/sunxi-default/headers-packing.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/scripts/package/builddeb b/scripts/package/builddeb -index 6c3b038..616aa9b 100755 ---- a/scripts/package/builddeb -+++ b/scripts/package/builddeb -@@ -334,6 +334,8 @@ mkdir -p "$destdir" - ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build" - rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles" - -+olddir="$(pwd)"; cd "$destdir"; make M=scripts clean; cd $olddir -+ - cat <> debian/control - - Package: $kernel_headers_packagename diff --git a/patch/kernel/sunxi-default/i2s_spdif_driver.patch b/patch/kernel/sunxi-default/i2s_spdif_driver.patch deleted file mode 100644 index 6f94dad884..0000000000 --- a/patch/kernel/sunxi-default/i2s_spdif_driver.patch +++ /dev/null @@ -1,2955 +0,0 @@ ---- a/include/sound/pcm.h -+++ b/include/sound/pcm.h -@@ -124,6 +124,10 @@ struct snd_pcm_ops { - #define SNDRV_PCM_RATE_96000 (1<<10) /* 96000Hz */ - #define SNDRV_PCM_RATE_176400 (1<<11) /* 176400Hz */ - #define SNDRV_PCM_RATE_192000 (1<<12) /* 192000Hz */ -+#define SNDRV_PCM_RATE_352800 (1<<13) /* 352800Hz */ -+#define SNDRV_PCM_RATE_384000 (1<<14) /* 384000Hz */ -+#define SNDRV_PCM_RATE_705600 (1<<15) /* 705600Hz */ -+#define SNDRV_PCM_RATE_768000 (1<<16) /* 768000Hz */ - - #define SNDRV_PCM_RATE_CONTINUOUS (1<<30) /* continuous range */ - #define SNDRV_PCM_RATE_KNOT (1<<31) /* supports more non-continuos rates */ -@@ -136,6 +140,10 @@ struct snd_pcm_ops { - SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000) - #define SNDRV_PCM_RATE_8000_192000 (SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\ - SNDRV_PCM_RATE_192000) -+#define SNDRV_PCM_RATE_8000_384000 (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_352800|\ -+ SNDRV_PCM_RATE_384000) -+#define SNDRV_PCM_RATE_8000_768000 (SNDRV_PCM_RATE_8000_384000|SNDRV_PCM_RATE_705600|\ -+ SNDRV_PCM_RATE_768000) - #define _SNDRV_PCM_FMTBIT(fmt) (1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt) - #define SNDRV_PCM_FMTBIT_S8 _SNDRV_PCM_FMTBIT(S8) - #define SNDRV_PCM_FMTBIT_U8 _SNDRV_PCM_FMTBIT(U8) - ---- a/sound/core/pcm_native.c -+++ b/sound/core/pcm_native.c -@@ -1382,8 +1382,6 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state) - if (! snd_pcm_playback_empty(substream)) { - snd_pcm_do_start(substream, SNDRV_PCM_STATE_DRAINING); - snd_pcm_post_start(substream, SNDRV_PCM_STATE_DRAINING); -- } else { -- runtime->status->state = SNDRV_PCM_STATE_SETUP; - } - break; - case SNDRV_PCM_STATE_RUNNING: -@@ -1765,12 +1763,13 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, - return snd_interval_refine(hw_param_interval(params, rule->var), &t); - } - --#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12 -+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_768000 != 1 << 16 - #error "Change this table" - #endif - - static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, -- 48000, 64000, 88200, 96000, 176400, 192000 }; -+ 48000, 64000, 88200, 96000, 176400, 192000, -+ 352800, 384000, 705600, 768000 }; - - const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { - .count = ARRAY_SIZE(rates), - ---- a/sound/soc/sunxi/i2s/sndi2s.c -+++ b/sound/soc/sunxi/i2s/sndi2s.c -@@ -40,7 +40,8 @@ static int sunxi_i2s_slave = 0; - #define sndi2s_RATES_MASTER (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) - #define sndi2s_RATES_SLAVE (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ -- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) -+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000|\ -+ SNDRV_PCM_RATE_352800 | SNDRV_PCM_RATE_384000) - - #if defined CONFIG_ARCH_SUN7I - #define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) -@@ -49,10 +50,6 @@ static int sunxi_i2s_slave = 0; - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_LE) - #endif - --/* cleaning code --hdmi_audio_t hdmi_parameter; --*/ -- - static int sndi2s_mute(struct snd_soc_dai *dai, int mute) - { - return 0; -@@ -74,26 +71,6 @@ static int sndi2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) - { --/* -- switch (params_format(params)) -- { -- case SNDRV_PCM_FORMAT_S16_LE: -- printk("[IIS-0] sndi2s_hw_params: format 16 bit\n"); -- break; -- case SNDRV_PCM_FORMAT_S20_3LE: -- printk("[IIS-0] sndi2s_hw_params: format 20 bit in 3 bytes\n"); -- break; -- case SNDRV_PCM_FORMAT_S24_LE: -- printk("[IIS-0] sndi2s_hw_params: format 24 bit in 4 bytes\n"); -- break; -- default: -- printk("[IIS-0] sndi2s_hw_params: Unsupported format (%d)\n", (int)params_format(params)); -- //return -EINVAL; -- } --*/ --/* cleaning code -- hdmi_parameter.sample_rate = params_rate(params); --*/ - return 0; - } - -@@ -105,18 +82,12 @@ static int sndi2s_set_dai_sysclk(struct snd_soc_dai *codec_dai, - - static int sndi2s_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) - { --/* cleaning code -- hdmi_parameter.fs_between = div; --*/ - return 0; - } - - static int sndi2s_set_dai_fmt(struct snd_soc_dai *codec_dai, - unsigned int fmt) - { --/* -- printk("[IIS-0] sndi2s_set_dai_fmt: format (%u)\n", fmt); --*/ - return 0; - } - -@@ -140,6 +111,13 @@ struct snd_soc_dai_driver sndi2s_dai = { - .rates = sndi2s_RATES_MASTER, - .formats = sndi2s_FORMATS, - }, -+ .capture = { -+ .stream_name = "Capture", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = sndi2s_RATES_MASTER, -+ .formats = sndi2s_FORMATS, -+ }, - /* pcm operations */ - .ops = &sndi2s_dai_ops, - .symmetric_rates = 1, -@@ -176,6 +154,7 @@ static int __devinit sndi2s_codec_probe(struct platform_device *pdev) - { - if(sunxi_i2s_slave) { - sndi2s_dai.playback.rates = sndi2s_RATES_SLAVE; -+ sndi2s_dai.capture.rates = sndi2s_RATES_SLAVE; - printk("[I2S-0] sndi2s_codec_probe I2S used in slave mode\n"); - } - else -@@ -210,7 +189,7 @@ static int __init sndi2s_codec_init(void) - - ret = script_parser_fetch("i2s_para","i2s_used", &i2s_used, sizeof(int)); - if (ret) { -- printk("[I2S]sndi2s_init fetch i2s using configuration failed\n"); -+ printk("[I2S-0] sndi2s_init fetch i2s using configuration failed\n"); - } - - if (i2s_used) { -@@ -230,7 +209,7 @@ static int __init sndi2s_codec_init(void) - if ((err = platform_driver_register(&sndi2s_codec_driver)) < 0) - return err; - } else { -- printk("[I2S]sndi2s cannot find any using configuration for controllers, return directly!\n"); -+ printk("[I2S-0] sndi2s cannot find any using configuration for controllers, return directly!\n"); - return 0; - } - ---- a/sound/soc/sunxi/i2s/sunxi-i2s.c -+++ b/sound/soc/sunxi/i2s/sunxi-i2s.c -@@ -38,7 +38,7 @@ - #include "sunxi-i2sdma.h" - #include "sunxi-i2s.h" - --static int regsave[8]; -+static int regsave[10]; - static int i2s_used = 0; - - static struct sunxi_dma_params sunxi_i2s_pcm_stereo_out = { -@@ -66,7 +66,8 @@ static struct clk *i2s_apbclk, *i2s_pll2clk, *i2s_pllx8, *i2s_moduleclk; - void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) - { - u32 reg_val; -- /*printk("[I2S-0] sunxi_snd_txctrl_i2s\n");*/ -+ int res; -+ printk("[I2S-0] sunxi_snd_txctrl_i2s is on=(%d)\n", on); - - reg_val = readl(sunxi_iis.regs + SUNXI_TXCHSEL); - reg_val &= ~0x7; -@@ -146,14 +147,13 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) - reg_val |= SUNXI_IISINT_TXDRQEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); - -- //Global Enable Digital Audio Interface -- reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -- if(sunxi_iis.slave) -- reg_val |= SUNXI_IISCTL_MS; // 1: Slave! -- reg_val |= SUNXI_IISCTL_GEN; -- writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); -+ //Disable mute -+ res = gpio_write_one_pin_value(i2s_handle, 0, "i2s_mute"); - - } else { -+ //Enable mute -+ res = gpio_write_one_pin_value(i2s_handle, 1, "i2s_mute"); -+ - /* IIS TX DISABLE */ - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - reg_val &= ~SUNXI_IISCTL_TXEN; -@@ -163,18 +163,27 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on) - reg_val = readl(sunxi_iis.regs + SUNXI_IISINT); - reg_val &= ~SUNXI_IISINT_TXDRQEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); -- -- //Global disable Digital Audio Interface -- reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -- reg_val &= ~SUNXI_IISCTL_GEN; -- writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - } - } - --void sunxi_snd_rxctrl_i2s(int on) -+void sunxi_snd_rxctrl_i2s(struct snd_pcm_substream *substream, int on) - { - u32 reg_val; -- /*printk("[I2S-0] sunxi_snd_rxctrl_i2s\n");*/ -+ printk("[I2S-0] sunxi_snd_rxctrl_i2s is on=(%d)\n", on); -+ reg_val = readl(sunxi_iis.regs + SUNXI_RXCHSEL); -+ reg_val &= ~0x7; -+ reg_val |= SUNXI_RXCHSEL_CHNUM(substream->runtime->channels); -+ writel(reg_val, sunxi_iis.regs + SUNXI_RXCHSEL); -+ -+ reg_val = readl(sunxi_iis.regs + SUNXI_RXCHMAP); -+ reg_val = 0; -+ if(substream->runtime->channels == 1) { -+ reg_val = 0x00003200; -+ } else { -+ reg_val = 0x00003210; -+ } -+ writel(reg_val, sunxi_iis.regs + SUNXI_RXCHMAP); -+ - //flush RX FIFO - reg_val = readl(sunxi_iis.regs + SUNXI_IISFCTL); - reg_val |= SUNXI_IISFCTL_FRX; -@@ -194,13 +203,6 @@ void sunxi_snd_rxctrl_i2s(int on) - reg_val |= SUNXI_IISINT_RXDRQEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); - -- //Global Enable Digital Audio Interface -- reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -- if(sunxi_iis.slave) -- reg_val |= SUNXI_IISCTL_MS; // 1: Slave! -- reg_val |= SUNXI_IISCTL_GEN; -- writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); -- - } else { - /* IIS RX DISABLE */ - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -@@ -211,26 +213,16 @@ void sunxi_snd_rxctrl_i2s(int on) - reg_val = readl(sunxi_iis.regs + SUNXI_IISINT); - reg_val &= ~SUNXI_IISINT_RXDRQEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISINT); -- -- //Global disable Digital Audio Interface -- reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -- reg_val &= ~SUNXI_IISCTL_GEN; -- writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - } - } - --//not used --/* --static inline int sunxi_snd_is_clkmaster(void) --{ -- return ((readl(sunxi_iis.regs + SUNXI_IISCTL) & SUNXI_IISCTL_MS) ? 0 : 1); --} --*/ - static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - { - u32 reg_val; - u32 reg_val1; - -+ printk("[IIS-0] sunxi_i2s_set_fmt\n"); -+ - //SDO ON - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - if (sunxi_is_sun4i() || sunxi_is_sun7i()) { -@@ -239,37 +231,24 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - } else { - reg_val |= SUNXI_IISCTL_SDO0EN; - } -- writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); -+ -+ //writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - - /* master or slave selection */ -- reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -+ //reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - if(sunxi_iis.slave) - { - reg_val |= SUNXI_IISCTL_MS; // 1: Slave! -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set slave mode for I2S interface\n");*/ -+ printk("[IIS-0] sunxi_i2s_set_fmt: set slave mode for I2S interface\n"); - } - else - { - reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set master mode for I2S interface\n");*/ -- } --/* -- switch(fmt & SND_SOC_DAIFMT_MASTER_MASK){ -- case SND_SOC_DAIFMT_CBS_CFS: // codec clk & frm slave -- reg_val |= SUNXI_IISCTL_MS; // 1: Slave! -- printk("[IIS-0] sunxi_i2s_set_fmt: set slave mode for I2S interface\n"); -- break; -- case SND_SOC_DAIFMT_CBM_CFM: // codec clk & frm master -- reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! -- printk("[IIS-0] sunxi_i2s_set_fmt: set master mode for I2S interface\n"); -- break; -- default: -- printk("[IIS-0] sunxi_i2s_set_fmt: not master or slave mode\n"); -- return -EINVAL; -+ printk("[IIS-0] sunxi_i2s_set_fmt: set master mode for I2S interface\n"); - } --*/ - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - -+ sunxi_iis.lrc_pol = 0; - /* pcm or i2s mode selection */ - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - reg_val1 = readl(sunxi_iis.regs + SUNXI_IISFAT0); -@@ -278,27 +257,33 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - case SND_SOC_DAIFMT_I2S: /* I2S mode */ - reg_val &= ~SUNXI_IISCTL_PCM; - reg_val1 |= SUNXI_IISFAT0_FMT_I2S; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set I2S mode\n");*/ -+ sunxi_iis.samp_format = 0; -+ printk("[IIS-0] sunxi_i2s_set_fmt: set I2S mode\n"); - break; - case SND_SOC_DAIFMT_RIGHT_J: /* Right Justified mode */ - reg_val &= ~SUNXI_IISCTL_PCM; - reg_val1 |= SUNXI_IISFAT0_FMT_RGT; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set Right Justified mode\n");*/ -+ sunxi_iis.samp_format = 2; -+ printk("[IIS-0] sunxi_i2s_set_fmt: set Right Justified mode\n"); - break; - case SND_SOC_DAIFMT_LEFT_J: /* Left Justified mode */ - reg_val &= ~SUNXI_IISCTL_PCM; - reg_val1 |= SUNXI_IISFAT0_FMT_LFT; -+ sunxi_iis.samp_format = 1; - /*printk("[IIS-0] sunxi_i2s_set_fmt: set Left Justified mode\n");*/ - break; - case SND_SOC_DAIFMT_DSP_A: /* L data msb after FRM LRC */ - reg_val |= SUNXI_IISCTL_PCM; - reg_val1 &= ~SUNXI_IISFAT0_LRCP; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb after FRM LRC mode\n");*/ -+ sunxi_iis.samp_format = 3; -+ printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb after FRM LRC mode\n"); - break; - case SND_SOC_DAIFMT_DSP_B: /* L data msb during FRM LRC */ - reg_val |= SUNXI_IISCTL_PCM; - reg_val1 |= SUNXI_IISFAT0_LRCP; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb during FRM LRC mode\n");*/ -+ sunxi_iis.samp_format = 3; -+ sunxi_iis.lrc_pol = 1; -+ printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb during FRM LRC mode\n"); - break; - default: - printk("[IIS-0] sunxi_i2s_set_fmt: unknown mode\n"); -@@ -313,22 +298,30 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */ - reg_val1 &= ~SUNXI_IISFAT0_LRCP; - reg_val1 &= ~SUNXI_IISFAT0_BCP; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: normal bit clock + frame\n");*/ -+ sunxi_iis.lrc_pol = 0; -+ sunxi_iis.bclk_pol = 0; -+ printk("[IIS-0] sunxi_i2s_set_fmt: normal bit clock + frame\n"); - break; - case SND_SOC_DAIFMT_NB_IF: /* normal bclk + inv frm */ - reg_val1 |= SUNXI_IISFAT0_LRCP; - reg_val1 &= ~SUNXI_IISFAT0_BCP; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: normal bclk + inv frm\n");*/ -+ sunxi_iis.lrc_pol = 1; -+ sunxi_iis.bclk_pol = 0; -+ printk("[IIS-0] sunxi_i2s_set_fmt: normal bclk + inv frm\n"); - break; - case SND_SOC_DAIFMT_IB_NF: /* invert bclk + nor frm */ - reg_val1 &= ~SUNXI_IISFAT0_LRCP; - reg_val1 |= SUNXI_IISFAT0_BCP; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + nor frm\n");*/ -+ sunxi_iis.lrc_pol = 0; -+ sunxi_iis.bclk_pol = 1; -+ printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + nor frm\n"); - break; - case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */ - reg_val1 |= SUNXI_IISFAT0_LRCP; - reg_val1 |= SUNXI_IISFAT0_BCP; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + frm\n");*/ -+ sunxi_iis.lrc_pol = 1; -+ sunxi_iis.bclk_pol = 1; -+ printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + frm\n"); - break; - } - writel(reg_val1, sunxi_iis.regs + SUNXI_IISFAT0); -@@ -336,7 +329,10 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - /* clear word select size */ - reg_val = readl(sunxi_iis.regs + SUNXI_IISFAT0); - reg_val &= ~SUNXI_IISFAT0_WSS_32BCLK; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: word size = %d\n", sunxi_iis.ws_size);*/ -+ /* word size hardcoded to 32 (ref. sunxi-sndi2s.c func. sunxi_sndi2s_hw_params()) */ -+ sunxi_iis.ws_size = 32; -+ printk("[IIS-0] sunxi_i2s_set_fmt: word size = %d\n", sunxi_iis.ws_size); -+ /* - if(sunxi_iis.ws_size == 16) - reg_val |= SUNXI_IISFAT0_WSS_16BCLK; - else if(sunxi_iis.ws_size == 20) -@@ -344,22 +340,25 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - else if(sunxi_iis.ws_size == 24) - reg_val |= SUNXI_IISFAT0_WSS_24BCLK; - else -- reg_val |= SUNXI_IISFAT0_WSS_32BCLK; -+ */ -+ reg_val |= SUNXI_IISFAT0_WSS_32BCLK; -+ - writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT0); - - /* PCM REGISTER setup */ -- reg_val = sunxi_iis.pcm_txtype&0x3; -- reg_val |= sunxi_iis.pcm_rxtype<<2; -+ reg_val = 0; -+ //reg_val = sunxi_iis.pcm_txtype&0x3; -+ //reg_val |= sunxi_iis.pcm_rxtype<<2; - - if(!sunxi_iis.pcm_sync_type) - { - reg_val |= SUNXI_IISFAT1_SSYNC; //short sync -- /*printk("[IIS-0] sunxi_i2s_set_fmt: set pcm_sync_type = short sync\n");*/ -+ printk("[IIS-0] sunxi_i2s_set_fmt: set pcm_sync_type = short sync\n"); - } - if(sunxi_iis.pcm_sw == 16) - { - reg_val |= SUNXI_IISFAT1_SW; -- /*printk("[IIS-0] sunxi_i2s_set_fmt: pcm_sw == 16\n");*/ -+ printk("[IIS-0] sunxi_i2s_set_fmt: pcm_sw == 16\n"); - } - - reg_val |=((sunxi_iis.pcm_start_slot - 1)&0x3)<<6; //start slot index -@@ -367,31 +366,31 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - reg_val |= sunxi_iis.pcm_lsb_first<<9; //MSB or LSB first - - if(sunxi_iis.pcm_sync_period == 256) -- reg_val |= 0x4<<12; -+ reg_val |= SUNXI_IISFAT1_SYNCLEN_256BCLK; - else if (sunxi_iis.pcm_sync_period == 128) -- reg_val |= 0x3<<12; -+ reg_val |= SUNXI_IISFAT1_SYNCLEN_128BCLK; - else if (sunxi_iis.pcm_sync_period == 64) -- reg_val |= 0x2<<12; -+ reg_val |= SUNXI_IISFAT1_SYNCLEN_64BCLK; - else if (sunxi_iis.pcm_sync_period == 32) -- reg_val |= 0x1<<12; -+ reg_val |= SUNXI_IISFAT1_SYNCLEN_32BCLK; - writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT1); - - /* set FIFO control register */ -- reg_val = 0 & 0x3; -- reg_val |= (1 & 0x1)<<2; -- reg_val |= SUNXI_IISFCTL_RXTL(0xf); //RX FIFO trigger level -- reg_val |= SUNXI_IISFCTL_TXTL(0x40); //TX FIFO empty trigger level -+ reg_val = 0; -+ reg_val |= SUNXI_IISFCTL_RXTL(0xf); //RX FIFO trigger level -+ reg_val |= SUNXI_IISFCTL_TXTL(0x40); //TX FIFO empty trigger level - writel(reg_val, sunxi_iis.regs + SUNXI_IISFCTL); - return 0; - } - - static int sunxi_i2s_hw_params(struct snd_pcm_substream *substream, -- struct snd_pcm_hw_params *params, -- struct snd_soc_dai *dai) -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) - { - u32 reg_val; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sunxi_dma_params *dma_data; -+ printk("[IIS-0] sunxi_i2s_hw_params: %s\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "recording"); - - /* play or record */ - if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -@@ -408,23 +407,39 @@ static int sunxi_i2s_hw_params(struct snd_pcm_substream *substream, - { - case SNDRV_PCM_FORMAT_S16_LE: - reg_val |= SUNXI_IISFAT0_SR_16BIT; -- /*printk("[IIS-0] sunxi_i2s_hw_params: format 16 bit\n");*/ -+ sunxi_iis.samp_res = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: - reg_val |= SUNXI_IISFAT0_SR_20BIT; -- /*printk("[IIS-0] sunxi_i2s_hw_params: format 20 bit\n");*/ -+ sunxi_iis.samp_res = 20; - break; - case SNDRV_PCM_FORMAT_S24_LE: - reg_val |= SUNXI_IISFAT0_SR_24BIT; -- /*printk("[IIS-0] sunxi_i2s_hw_params: format 24 bit\n");*/ -+ sunxi_iis.samp_res = 24; - break; - default: -- printk("[IIS-0] sunxi_i2s_hw_params: Unsupported format (%d)\n", (int)params_format(params)); -- //return -EINVAL; -+ pr_err("[IIS-0] sunxi_i2s_hw_params: Unsupported format (%d)\n", (int)params_format(params)); -+ return -EINVAL; - } - writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT0); - -+ /* set FIFO control register */ -+ reg_val = readl(sunxi_iis.regs + SUNXI_IISFCTL); -+ reg_val |= SUNXI_IISFCTL_TXIM_MOD1; //1: Valid data at the LSB of TXFIFO register -+ //CHECK EXPANDING FORMAT!!! -+ if(sunxi_iis.samp_res == 24) { -+ reg_val &= ~SUNXI_IISFCTL_RXOM_MOD3; //00: Expanding 0 at LSB of DA_RXFIFO register -+ } -+ else { -+ reg_val |= SUNXI_IISFCTL_RXOM_MOD1; //00: Expanding 0 at LSB of DA_RXFIFO register -+ } -+ writel(reg_val, sunxi_iis.regs + SUNXI_IISFCTL); -+ - snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); -+ sunxi_iis.samp_fs = params_rate(params); -+ sunxi_iis.channel_num = params_channels(params); -+ printk("[IIS-0] sunxi_i2s_hw_params: channel num %d, format %d bit, sample rate %d\n", -+ sunxi_iis.channel_num, sunxi_iis.samp_res, sunxi_iis.samp_fs); - return 0; - } - -@@ -441,7 +456,7 @@ static int sunxi_i2s_trigger(struct snd_pcm_substream *substream, - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { -- sunxi_snd_rxctrl_i2s(1); -+ sunxi_snd_rxctrl_i2s(substream, 1); - } else { - sunxi_snd_txctrl_i2s(substream, 1); - } -@@ -451,7 +466,7 @@ static int sunxi_i2s_trigger(struct snd_pcm_substream *substream, - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { -- sunxi_snd_rxctrl_i2s(0); -+ sunxi_snd_rxctrl_i2s(substream, 0); - } else { - sunxi_snd_txctrl_i2s(substream, 0); - } -@@ -556,6 +571,10 @@ static int sunxi_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div - gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div1"); - gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div0"); - } -+ else if(div == 64) { -+ gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div1"); -+ gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div0"); -+ } - else { - pr_err("[I2S-0] try to set unsupported external clock divider div=(%d)\n", div); - return -EINVAL; -@@ -599,6 +618,8 @@ static void iisregsave(void) - regsave[5] = readl(sunxi_iis.regs + SUNXI_IISCLKD); - regsave[6] = readl(sunxi_iis.regs + SUNXI_TXCHSEL); - regsave[7] = readl(sunxi_iis.regs + SUNXI_TXCHMAP); -+ regsave[8] = readl(sunxi_iis.regs + SUNXI_RXCHSEL); -+ regsave[9] = readl(sunxi_iis.regs + SUNXI_RXCHMAP); - } - - static void iisregrestore(void) -@@ -611,12 +632,14 @@ static void iisregrestore(void) - writel(regsave[5], sunxi_iis.regs + SUNXI_IISCLKD); - writel(regsave[6], sunxi_iis.regs + SUNXI_TXCHSEL); - writel(regsave[7], sunxi_iis.regs + SUNXI_TXCHMAP); -+ writel(regsave[8], sunxi_iis.regs + SUNXI_RXCHSEL); -+ writel(regsave[9], sunxi_iis.regs + SUNXI_RXCHMAP); - } - - static int sunxi_i2s_suspend(struct snd_soc_dai *cpu_dai) - { - u32 reg_val; -- printk("[IIS]Entered %s\n", __func__); -+ printk("[I2S-0] Entered %s\n", __func__); - - //Global Disable Digital Audio Interface - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -@@ -632,15 +655,15 @@ static int sunxi_i2s_suspend(struct snd_soc_dai *cpu_dai) - clk_disable(i2s_apbclk); - - //printk("[IIS]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); -- printk("[IIS]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); -- printk("[IIS]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); -+ printk("[I2S-0] SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); -+ printk("[I2S-0] SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); - - return 0; - } - static int sunxi_i2s_resume(struct snd_soc_dai *cpu_dai) - { - u32 reg_val; -- printk("[IIS]Entered %s\n", __func__); -+ printk("[I2S-0] Entered %s\n", __func__); - - //enable the module clock - clk_enable(i2s_apbclk); -@@ -657,12 +680,14 @@ static int sunxi_i2s_resume(struct snd_soc_dai *cpu_dai) - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - if(sunxi_iis.slave) - reg_val |= SUNXI_IISCTL_MS; // 1: Slave! -+ else -+ reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! - reg_val |= SUNXI_IISCTL_GEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - - //printk("[IIS]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); -- printk("[IIS]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); -- printk("[IIS]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); -+ printk("[I2S-0] SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); -+ printk("[I2S-0] SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__); - - return 0; - } -@@ -670,7 +695,8 @@ static int sunxi_i2s_resume(struct snd_soc_dai *cpu_dai) - #define SUNXI_I2S_RATES_MASTER (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) - #define SUNXI_I2S_RATES_SLAVE (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\ -- SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) -+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 |\ -+ SNDRV_PCM_RATE_352800 | SNDRV_PCM_RATE_384000) - static struct snd_soc_dai_ops sunxi_iis_dai_ops = { - .trigger = sunxi_i2s_trigger, - .hw_params = sunxi_i2s_hw_params, -@@ -704,7 +730,7 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) - { - int reg_val = 0; - int ret; -- printk("[IIS]Entered %s\n", __func__); -+ printk("[I2S-0] Entered %s\n", __func__); - - sunxi_iis.regs = ioremap(SUNXI_IISBASE, 0x100); - if (sunxi_iis.regs == NULL) -@@ -720,7 +746,7 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) - ); - - if(-1 == clk_enable(i2s_apbclk)){ -- printk("i2s_apbclk failed! line = %d\n", __LINE__); -+ pr_err("[I2S-0] i2s_apbclk failed! line = %d\n", __LINE__); - goto out; - } - -@@ -739,25 +765,30 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) - ); - - if(clk_set_parent(i2s_moduleclk, i2s_pll2clk)){ -- printk("try to set parent of i2s_moduleclk to i2s_pll2ck failed! line = %d\n",__LINE__); -+ pr_err("[I2S-0] try to set parent of i2s_moduleclk to i2s_pll2ck failed! line = %d\n",__LINE__); - goto out1; - } - - if(clk_set_rate(i2s_moduleclk, 24576000/8)){ -- printk("set i2s_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); -+ pr_err("[I2S-0] set i2s_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); - goto out1; - } - - if(-1 == clk_enable(i2s_moduleclk)){ -- printk("open i2s_moduleclk failed! line = %d\n", __LINE__); -+ pr_err("[I2S-0] open i2s_moduleclk failed! line = %d\n", __LINE__); - goto out1; - } - } else -- sunxi_iis_dai.playback.rates = SUNXI_I2S_RATES_SLAVE; -+ { -+ sunxi_iis_dai.playback.rates = SUNXI_I2S_RATES_SLAVE; -+ sunxi_iis_dai.capture.rates = SUNXI_I2S_RATES_SLAVE; -+ } - - reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); - if(sunxi_iis.slave) - reg_val |= SUNXI_IISCTL_MS; // 1: Slave! -+ else -+ reg_val &= ~SUNXI_IISCTL_MS; // 0: Master! - reg_val |= SUNXI_IISCTL_GEN; - writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); - -@@ -780,9 +811,15 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev) - - static int __devexit sunxi_i2s_dev_remove(struct platform_device *pdev) - { -- printk("[IIS]Entered %s\n", __func__); -+ int reg_val = 0; -+ printk("[I2S-0] Entered %s\n", __func__); - - if(i2s_used) { -+ //Global disable Digital Audio Interface -+ reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL); -+ reg_val &= ~SUNXI_IISCTL_GEN; -+ writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL); -+ - i2s_used = 0; - if(!sunxi_iis.slave) { - //release the module clock -@@ -824,11 +861,11 @@ static int __init sunxi_i2s_init(void) - int err = 0, i2s_slave = 0; - int ret; - -- printk("[IIS]Entered %s\n", __func__); -+ printk("[I2S-0] Entered %s\n", __func__); - - ret = script_parser_fetch("i2s_para","i2s_used", &i2s_used, sizeof(int)); - if (ret) { -- printk("[I2S]sunxi_i2s_init fetch i2s using configuration failed\n"); -+ printk("[I2S-0] sunxi_i2s_init fetch i2s using configuration failed\n"); - } - - if (i2s_used) { -@@ -849,7 +886,7 @@ static int __init sunxi_i2s_init(void) - if ((err = platform_driver_register(&sunxi_i2s_driver)) < 0) - return err; - } else { -- printk("[I2S]sunxi-i2s cannot find any using configuration for controllers, return directly!\n"); -+ printk("[I2S-0] sunxi-i2s cannot find any using configuration for controllers, return directly!\n"); - return 0; - } - return 0; -@@ -858,7 +895,7 @@ module_init(sunxi_i2s_init); - - static void __exit sunxi_i2s_exit(void) - { -- printk("[IIS]Entered %s\n", __func__); -+ printk("[I2S-0] Entered %s\n", __func__); - platform_driver_unregister(&sunxi_i2s_driver); - } - module_exit(sunxi_i2s_exit); ---- a/sound/soc/sunxi/i2s/sunxi-i2s.h -+++ b/sound/soc/sunxi/i2s/sunxi-i2s.h -@@ -20,71 +20,71 @@ - /* REGISTER definition */ - - /* IIS REGISTER */ --#define SUNXI_IISBASE (0x01C22400) -+#define SUNXI_IISBASE (0x01C22400) - --#define SUNXI_IISCTL (0x00) -+#define SUNXI_IISCTL (0x00) - #define SUNXI_IISCTL_SDO3EN (1<<11) - #define SUNXI_IISCTL_SDO2EN (1<<10) - #define SUNXI_IISCTL_SDO1EN (1<<9) - #define SUNXI_IISCTL_SDO0EN (1<<8) -- #define SUNXI_IISCTL_ASS (1<<6) -+ #define SUNXI_IISCTL_ASS (1<<6) - #define SUNXI_IISCTL_MS (1<<5) -- #define SUNXI_IISCTL_PCM (1<<4) -- #define SUNXI_IISCTL_LOOP (1<<3) -- #define SUNXI_IISCTL_TXEN (1<<2) -- #define SUNXI_IISCTL_RXEN (1<<1) -- #define SUNXI_IISCTL_GEN (1<<0) -- --#define SUNXI_IISFAT0 (0x04) -- #define SUNXI_IISFAT0_LRCP (1<<7) -- #define SUNXI_IISFAT0_BCP (1<<6) -- #define SUNXI_IISFAT0_SR_RVD (3<<4) -- #define SUNXI_IISFAT0_SR_16BIT (0<<4) -- #define SUNXI_IISFAT0_SR_20BIT (1<<4) -- #define SUNXI_IISFAT0_SR_24BIT (2<<4) -- #define SUNXI_IISFAT0_WSS_16BCLK (0<<2) -- #define SUNXI_IISFAT0_WSS_20BCLK (1<<2) -- #define SUNXI_IISFAT0_WSS_24BCLK (2<<2) -- #define SUNXI_IISFAT0_WSS_32BCLK (3<<2) -- #define SUNXI_IISFAT0_FMT_I2S (0<<0) -- #define SUNXI_IISFAT0_FMT_LFT (1<<0) -- #define SUNXI_IISFAT0_FMT_RGT (2<<0) -- #define SUNXI_IISFAT0_FMT_RVD (3<<0) -- --#define SUNXI_IISFAT1 (0x08) -- #define SUNXI_IISFAT1_SYNCLEN_16BCLK (0<<12) -- #define SUNXI_IISFAT1_SYNCLEN_32BCLK (1<<12) -- #define SUNXI_IISFAT1_SYNCLEN_64BCLK (2<<12) -- #define SUNXI_IISFAT1_SYNCLEN_128BCLK (3<<12) -- #define SUNXI_IISFAT1_SYNCLEN_256BCLK (4<<12) -- #define SUNXI_IISFAT1_SYNCOUTEN (1<<11) -- #define SUNXI_IISFAT1_OUTMUTE (1<<10) -- #define SUNXI_IISFAT1_MLS (1<<9) -- #define SUNXI_IISFAT1_SEXT (1<<8) -- #define SUNXI_IISFAT1_SI_1ST (0<<6) -- #define SUNXI_IISFAT1_SI_2ND (1<<6) -- #define SUNXI_IISFAT1_SI_3RD (2<<6) -- #define SUNXI_IISFAT1_SI_4TH (3<<6) -- #define SUNXI_IISFAT1_SW (1<<5) -- #define SUNXI_IISFAT1_SSYNC (1<<4) -- #define SUNXI_IISFAT1_RXPDM_16PCM (0<<2) -- #define SUNXI_IISFAT1_RXPDM_8PCM (1<<2) -- #define SUNXI_IISFAT1_RXPDM_8ULAW (2<<2) -- #define SUNXI_IISFAT1_RXPDM_8ALAW (3<<2) -- #define SUNXI_IISFAT1_TXPDM_16PCM (0<<0) -- #define SUNXI_IISFAT1_TXPDM_8PCM (1<<0) -- #define SUNXI_IISFAT1_TXPDM_8ULAW (2<<0) -- #define SUNXI_IISFAT1_TXPDM_8ALAW (3<<0) -- --#define SUNXI_IISTXFIFO (0x0C) -- --#define SUNXI_IISRXFIFO (0x10) -- --#define SUNXI_IISFCTL (0x14) -- #define SUNXI_IISFCTL_FIFOSRC (1<<31) -- #define SUNXI_IISFCTL_FTX (1<<25) -- #define SUNXI_IISFCTL_FRX (1<<24) -- #define SUNXI_IISFCTL_TXTL(v) ((v)<<12) -+ #define SUNXI_IISCTL_PCM (1<<4) -+ #define SUNXI_IISCTL_LOOP (1<<3) -+ #define SUNXI_IISCTL_TXEN (1<<2) -+ #define SUNXI_IISCTL_RXEN (1<<1) -+ #define SUNXI_IISCTL_GEN (1<<0) -+ -+#define SUNXI_IISFAT0 (0x04) -+ #define SUNXI_IISFAT0_LRCP (1<<7) -+ #define SUNXI_IISFAT0_BCP (1<<6) -+ #define SUNXI_IISFAT0_SR_RVD (3<<4) -+ #define SUNXI_IISFAT0_SR_16BIT (0<<4) -+ #define SUNXI_IISFAT0_SR_20BIT (1<<4) -+ #define SUNXI_IISFAT0_SR_24BIT (2<<4) -+ #define SUNXI_IISFAT0_WSS_16BCLK (0<<2) -+ #define SUNXI_IISFAT0_WSS_20BCLK (1<<2) -+ #define SUNXI_IISFAT0_WSS_24BCLK (2<<2) -+ #define SUNXI_IISFAT0_WSS_32BCLK (3<<2) -+ #define SUNXI_IISFAT0_FMT_I2S (0<<0) -+ #define SUNXI_IISFAT0_FMT_LFT (1<<0) -+ #define SUNXI_IISFAT0_FMT_RGT (2<<0) -+ #define SUNXI_IISFAT0_FMT_RVD (3<<0) -+ -+#define SUNXI_IISFAT1 (0x08) -+ #define SUNXI_IISFAT1_SYNCLEN_16BCLK (0<<12) -+ #define SUNXI_IISFAT1_SYNCLEN_32BCLK (1<<12) -+ #define SUNXI_IISFAT1_SYNCLEN_64BCLK (2<<12) -+ #define SUNXI_IISFAT1_SYNCLEN_128BCLK (3<<12) -+ #define SUNXI_IISFAT1_SYNCLEN_256BCLK (4<<12) -+ #define SUNXI_IISFAT1_SYNCOUTEN (1<<11) -+ #define SUNXI_IISFAT1_OUTMUTE (1<<10) -+ #define SUNXI_IISFAT1_MLS (1<<9) -+ #define SUNXI_IISFAT1_SEXT (1<<8) -+ #define SUNXI_IISFAT1_SI_1ST (0<<6) -+ #define SUNXI_IISFAT1_SI_2ND (1<<6) -+ #define SUNXI_IISFAT1_SI_3RD (2<<6) -+ #define SUNXI_IISFAT1_SI_4TH (3<<6) -+ #define SUNXI_IISFAT1_SW (1<<5) -+ #define SUNXI_IISFAT1_SSYNC (1<<4) -+ #define SUNXI_IISFAT1_RXPDM_16PCM (0<<2) -+ #define SUNXI_IISFAT1_RXPDM_8PCM (1<<2) -+ #define SUNXI_IISFAT1_RXPDM_8ULAW (2<<2) -+ #define SUNXI_IISFAT1_RXPDM_8ALAW (3<<2) -+ #define SUNXI_IISFAT1_TXPDM_16PCM (0<<0) -+ #define SUNXI_IISFAT1_TXPDM_8PCM (1<<0) -+ #define SUNXI_IISFAT1_TXPDM_8ULAW (2<<0) -+ #define SUNXI_IISFAT1_TXPDM_8ALAW (3<<0) -+ -+#define SUNXI_IISTXFIFO (0x0C) -+ -+#define SUNXI_IISRXFIFO (0x10) -+ -+#define SUNXI_IISFCTL (0x14) -+ #define SUNXI_IISFCTL_FIFOSRC (1<<31) -+ #define SUNXI_IISFCTL_FTX (1<<25) -+ #define SUNXI_IISFCTL_FRX (1<<24) -+ #define SUNXI_IISFCTL_TXTL(v) ((v)<<12) - #define SUNXI_IISFCTL_RXTL(v) ((v)<<4) - #define SUNXI_IISFCTL_TXIM_MOD0 (0<<2) - #define SUNXI_IISFCTL_TXIM_MOD1 (1<<2) -@@ -93,168 +93,94 @@ - #define SUNXI_IISFCTL_RXOM_MOD2 (2<<0) - #define SUNXI_IISFCTL_RXOM_MOD3 (3<<0) - --#define SUNXI_IISFSTA (0x18) -- #define SUNXI_IISFSTA_TXE (1<<28) -+#define SUNXI_IISFSTA (0x18) -+ #define SUNXI_IISFSTA_TXE (1<<28) - #define SUNXI_IISFSTA_TXECNT(v) ((v)<<16) -- #define SUNXI_IISFSTA_RXA (1<<8) -+ #define SUNXI_IISFSTA_RXA (1<<8) - #define SUNXI_IISFSTA_RXACNT(v) ((v)<<0) - --#define SUNXI_IISINT (0x1C) -- #define SUNXI_IISINT_TXDRQEN (1<<7) -- #define SUNXI_IISINT_TXUIEN (1<<6) -- #define SUNXI_IISINT_TXOIEN (1<<5) -- #define SUNXI_IISINT_TXEIEN (1<<4) -- #define SUNXI_IISINT_RXDRQEN (1<<2) -- #define SUNXI_IISINT_RXOIEN (1<<1) -- #define SUNXI_IISINT_RXAIEN (1<<0) -- --#define SUNXI_IISISTA (0x20) -- #define SUNXI_IISISTA_TXUISTA (1<<6) -- #define SUNXI_IISISTA_TXOISTA (1<<5) -- #define SUNXI_IISISTA_TXEISTA (1<<4) -- #define SUNXI_IISISTA_RXOISTA (1<<1) -- #define SUNXI_IISISTA_RXAISTA (1<<0) -- --#define SUNXI_IISCLKD (0x24) -- #define SUNXI_IISCLKD_MCLKOEN (1<<7) -+#define SUNXI_IISINT (0x1C) -+ #define SUNXI_IISINT_TXDRQEN (1<<7) -+ #define SUNXI_IISINT_TXUIEN (1<<6) -+ #define SUNXI_IISINT_TXOIEN (1<<5) -+ #define SUNXI_IISINT_TXEIEN (1<<4) -+ #define SUNXI_IISINT_RXDRQEN (1<<3) -+ #define SUNXI_IISINT_RXUIEN (1<<2) -+ #define SUNXI_IISINT_RXOIEN (1<<1) -+ #define SUNXI_IISINT_RXAIEN (1<<0) -+ -+#define SUNXI_IISISTA (0x20) -+ #define SUNXI_IISISTA_TXUISTA (1<<6) -+ #define SUNXI_IISISTA_TXOISTA (1<<5) -+ #define SUNXI_IISISTA_TXEISTA (1<<4) -+ #define SUNXI_IISISTA_RXUISTA (1<<2) -+ #define SUNXI_IISISTA_RXOISTA (1<<1) -+ #define SUNXI_IISISTA_RXAISTA (1<<0) -+ -+#define SUNXI_IISCLKD (0x24) -+ #define SUNXI_IISCLKD_MCLKOEN (1<<7) - #define SUNXI_IISCLKD_BCLKDIV_2 (0<<4) - #define SUNXI_IISCLKD_BCLKDIV_4 (1<<4) - #define SUNXI_IISCLKD_BCLKDIV_6 (2<<4) - #define SUNXI_IISCLKD_BCLKDIV_8 (3<<4) -- #define SUNXI_IISCLKD_BCLKDIV_12 (4<<4) -- #define SUNXI_IISCLKD_BCLKDIV_16 (5<<4) -- #define SUNXI_IISCLKD_BCLKDIV_32 (6<<4) -- #define SUNXI_IISCLKD_BCLKDIV_64 (7<<4) -+ #define SUNXI_IISCLKD_BCLKDIV_12 (4<<4) -+ #define SUNXI_IISCLKD_BCLKDIV_16 (5<<4) -+ #define SUNXI_IISCLKD_BCLKDIV_32 (6<<4) -+ #define SUNXI_IISCLKD_BCLKDIV_64 (7<<4) - #define SUNXI_IISCLKD_MCLKDIV_1 (0<<0) - #define SUNXI_IISCLKD_MCLKDIV_2 (1<<0) - #define SUNXI_IISCLKD_MCLKDIV_4 (2<<0) - #define SUNXI_IISCLKD_MCLKDIV_6 (3<<0) - #define SUNXI_IISCLKD_MCLKDIV_8 (4<<0) -- #define SUNXI_IISCLKD_MCLKDIV_12 (5<<0) -- #define SUNXI_IISCLKD_MCLKDIV_16 (6<<0) -- #define SUNXI_IISCLKD_MCLKDIV_24 (7<<0) -- #define SUNXI_IISCLKD_MCLKDIV_32 (8<<0) -- #define SUNXI_IISCLKD_MCLKDIV_48 (9<<0) -- #define SUNXI_IISCLKD_MCLKDIV_64 (10<<0) -- --#define SUNXI_IISTXCNT (0x28) -- --#define SUNXI_IISRXCNT (0x2C) -- --#define SUNXI_TXCHSEL (0x30) -- #define SUNXI_TXCHSEL_CHNUM(v) (((v)-1)<<0) -- --#define SUNXI_TXCHMAP (0x34) -- #define SUNXI_TXCHMAP_CH7(v) (((v)-1)<<28) -- #define SUNXI_TXCHMAP_CH6(v) (((v)-1)<<24) -- #define SUNXI_TXCHMAP_CH5(v) (((v)-1)<<20) -- #define SUNXI_TXCHMAP_CH4(v) (((v)-1)<<16) -- #define SUNXI_TXCHMAP_CH3(v) (((v)-1)<<12) -- #define SUNXI_TXCHMAP_CH2(v) (((v)-1)<<8) -- #define SUNXI_TXCHMAP_CH1(v) (((v)-1)<<4) -- #define SUNXI_TXCHMAP_CH0(v) (((v)-1)<<0) -- --#define SUNXI_RXCHSEL (0x38) -- #define SUNXI_RXCHSEL_CHNUM(v) (((v)-1)<<0) -- --#define SUNXI_RXCHMAP (0x3C) -- #define SUNXI_RXCHMAP_CH3(v) (((v)-1)<<12) -- #define SUNXI_RXCHMAP_CH2(v) (((v)-1)<<8) -- #define SUNXI_RXCHMAP_CH1(v) (((v)-1)<<4) -- #define SUNXI_RXCHMAP_CH0(v) (((v)-1)<<0) -- -- --/* DMA REGISTER */ --#define SUNXI_DMABASE (0x01C02000) -- --#define SUNXI_DMAIRQEN (0x0) -- #define SUNXI_DMAIRQEN_NDMA_FULLEN(v) (1<<((v)*2+1)) -- #define SUNXI_DMAIRQEN_NDMA_HALFEN(v) (1<<((v)*2)) -- --#define SUNXI_DMAIRQPENDING (0x4) -- #define SUNXI_DMAIRQPENGDING_NDMA_FULLPEND(v) (1<<((v)*2+1)) -- #define SUNXI_DMAIRQPENGDING_NDMA_HALFPEND(v) (1<<((v)*2)) -- --#define SUNXI_NDMACFG(v) ((v)*0x20+0x100) -- #define SUNXI_NDMACFG_DMALOAD (1<<31) -- #define SUNXI_NDMACFG_BUSY (1<<30) -- #define SUNXI_NDMACFG_CONTINUOUS (1<<29) -- #define SUNXI_NDMACFG_WAIT(v) (((v)-1)<<26) //wait clock = 2^n example: 8 clocks = 2^3 -- #define SUNXI_NDMACFG_DSTDATAWIDTH_8BIT (0<<24) -- #define SUNXI_NDMACFG_DSTDATAWIDTH_16BIT (1<<24) -- #define SUNXI_NDMACFG_DSTDATAWIDTH_32BIT (2<<24) -- #define SUNXI_NDMACFG_DSTDATAWIDTH_RVD (3<<24) -- #define SUNXI_NDMACFG_DSTBURST4 (1<<23) -- #define SUNXI_NDMACFG_DSTADDRTYPE_INC (0<<21) -- #define SUNXI_NDMACFG_DSTADDRTYPE_CON (1<<21) -- #define SUNXI_NDMACFG_DSTTYPE_IRTX (0x0<<16) -- #define SUNXI_NDMACFG_DSTTYPE_SPDIFTX (0x1<<16) -- #define SUNXI_NDMACFG_DSTTYPE_IISTX (0x2<<16) -- #define SUNXI_NDMACFG_DSTTYPE_AC97TX (0x3<<16) -- #define SUNXI_NDMACFG_DSTTYPE_SPI0TX (0x4<<16) -- #define SUNXI_NDMACFG_DSTTYPE_SPI1TX (0x5<<16) -- #define SUNXI_NDMACFG_DSTTYPE_SPI2TX (0x6<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART0TX (0x8<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART1TX (0x9<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART2TX (0xA<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART3TX (0xB<<16) -- #define SUNXI_NDMACFG_DSTTYPE_AUDIODA (0xC<<16) -- #define SUNXI_NDMACFG_DSTTYPE_NFC (0xF<<16) -- #define SUNXI_NDMACFG_DSTTYPE_SRAM (0x10<<16) -- #define SUNXI_NDMACFG_DSTTYPE_DRAM (0x11<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART4TX (0x12<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART5TX (0x13<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART6TX (0x14<<16) -- #define SUNXI_NDMACFG_DSTTYPE_UART7TX (0x15<<16) -- #define SUNXI_NDMACFG_SRCDATAWIDTH_8BIT (0<<8) -- #define SUNXI_NDMACFG_SRCDATAWIDTH_16BIT (1<<8) -- #define SUNXI_NDMACFG_SRCDATAWIDTH_32BIT (2<<8) -- #define SUNXI_NDMACFG_SRCDATAWIDTH_RVD (3<<8) -- #define SUNXI_NDMACFG_SRCBURST4 (1<<7) -- #define SUNXI_NDMACFG_SRCADDRTYPE_INC (0<<5) -- #define SUNXI_NDMACFG_SRCADDRTYPE_CON (1<<5) -- #define SUNXI_NDMACFG_SRCTYPE_IRRX (0x0<<0) -- #define SUNXI_NDMACFG_SRCTYPE_SPDIFRX (0x1<<0) -- #define SUNXI_NDMACFG_SRCTYPE_IISRX (0x2<<0) -- #define SUNXI_NDMACFG_SRCTYPE_AC97RX (0x3<<0) -- #define SUNXI_NDMACFG_SRCTYPE_SPI0RX (0x4<<0) -- #define SUNXI_NDMACFG_SRCTYPE_SPI1RX (0x5<<0) -- #define SUNXI_NDMACFG_SRCTYPE_SPI2RX (0x6<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART0RX (0x8<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART1RX (0x9<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART2RX (0xA<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART3RX (0xB<<0) -- #define SUNXI_NDMACFG_SRCTYPE_AUDIOAD (0xC<<0) -- #define SUNXI_NDMACFG_SRCTYPE_TPAD (0xD<<0) -- #define SUNXI_NDMACFG_SRCTYPE_NFC (0xF<<0) -- #define SUNXI_NDMACFG_SRCTYPE_SRAM (0x10<<0) -- #define SUNXI_NDMACFG_SRCTYPE_DRAM (0x11<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART4RX (0x12<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART5RX (0x13<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART6RX (0x14<<0) -- #define SUNXI_NDMACFG_SRCTYPE_UART7RX (0x15<<0) -- --#define SUNXI_NDMASRCADDR(v) ((v)*0x20 + 0x100 + 4) -- --#define SUNXI_NDMADSTADDR(v) ((v)*0x20 + 0x100 + 8) -- --#define SUNXI_NDMACNT(v) ((v)*0x20 + 0x100 + 0xC) -+ #define SUNXI_IISCLKD_MCLKDIV_12 (5<<0) -+ #define SUNXI_IISCLKD_MCLKDIV_16 (6<<0) -+ #define SUNXI_IISCLKD_MCLKDIV_24 (7<<0) -+ #define SUNXI_IISCLKD_MCLKDIV_32 (8<<0) -+ #define SUNXI_IISCLKD_MCLKDIV_48 (9<<0) -+ #define SUNXI_IISCLKD_MCLKDIV_64 (10<<0) -+ -+#define SUNXI_IISTXCNT (0x28) -+ -+#define SUNXI_IISRXCNT (0x2C) -+ -+#define SUNXI_TXCHSEL (0x30) -+ #define SUNXI_TXCHSEL_CHNUM(v) (((v)-1)<<0) -+ -+#define SUNXI_TXCHMAP (0x34) -+ #define SUNXI_TXCHMAP_CH7(v) (((v)-1)<<28) -+ #define SUNXI_TXCHMAP_CH6(v) (((v)-1)<<24) -+ #define SUNXI_TXCHMAP_CH5(v) (((v)-1)<<20) -+ #define SUNXI_TXCHMAP_CH4(v) (((v)-1)<<16) -+ #define SUNXI_TXCHMAP_CH3(v) (((v)-1)<<12) -+ #define SUNXI_TXCHMAP_CH2(v) (((v)-1)<<8) -+ #define SUNXI_TXCHMAP_CH1(v) (((v)-1)<<4) -+ #define SUNXI_TXCHMAP_CH0(v) (((v)-1)<<0) -+ -+#define SUNXI_RXCHSEL (0x38) -+ #define SUNXI_RXCHSEL_CHNUM(v) (((v)-1)<<0) -+ -+#define SUNXI_RXCHMAP (0x3C) -+ #define SUNXI_RXCHMAP_CH3(v) (((v)-1)<<12) -+ #define SUNXI_RXCHMAP_CH2(v) (((v)-1)<<8) -+ #define SUNXI_RXCHMAP_CH1(v) (((v)-1)<<4) -+ #define SUNXI_RXCHMAP_CH0(v) (((v)-1)<<0) -+ - - - /* CCM REGISTER */ --#define SUNXI_CCMBASE (0x01C20000) -+#define SUNXI_CCMBASE (0x01C20000) - --#define SUNXI_CCM_AUDIO_HOSC_PLL_REG (0x08) -+#define SUNXI_CCM_AUDIO_HOSC_PLL_REG (0x08) - #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_AUDIOEN (1<<31) - #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE225792MHZ (0<<27) - #define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE24576MHZ (1<<27) - --#define SUNXI_CCM_APB_GATE_REG (0x68) -- #define SUNXI_CCM_APB_GATE_REG_IISGATE (1<<3) -+#define SUNXI_CCM_APB_GATE_REG (0x68) -+ #define SUNXI_CCM_APB_GATE_REG_IISGATE (1<<3) - --#define SUNXI_CCM_AUDIO_CLK_REG (0xb8) -+#define SUNXI_CCM_AUDIO_CLK_REG (0xb8) - #define SUNXI_CCM_AUDIO_CLK_REG_IISSPECIALGATE (1<<31) -- #define SUNXI_CCM_AUDIO_CLK_REG_DIV(v) ((v)<<16) -+ #define SUNXI_CCM_AUDIO_CLK_REG_DIV(v) ((v)<<16) - /*------------------------------------------------------------*/ - - /*------------------------------------------------------------*/ -@@ -270,34 +196,32 @@ - #define SUNXI_IISCLKD_MCLKEN_OFFS 7 - - unsigned int sunxi_i2s_get_clockrate(void); --//extern struct sunxi_i2s_info sunxi_iis; --//extern struct snd_soc_dai sunxi_iis_dai; - - extern void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on); --extern void sunxi_snd_rxctrl_i2s(int on); -+extern void sunxi_snd_rxctrl_i2s(struct snd_pcm_substream *substream, int on); - - struct sunxi_i2s_info { -- void __iomem *regs; /* IIS BASE */ -- void __iomem *ccmregs; //CCM BASE -- void __iomem *ioregs; //IO BASE -+ void __iomem *regs; //IIS BASE -+ void __iomem *ccmregs; //CCM BASE -+ void __iomem *ioregs; //IO BASE - -- u32 slave; //0: master, 1: slave -- u32 mono; //0: stereo, 1: mono -- u32 samp_fs; //audio sample rate (unit in kHz) -+ u32 slave; //0: master, 1: slave -+ u32 channel_num; // -+ u32 samp_fs; //audio sample rate (unit in kHz) - u32 samp_res; //16 bits, 20 bits , 24 bits, 32 bits) - u32 samp_format; //audio sample format (0: standard I2S, 1: left-justified, 2: right-justified, 3: pcm) -- u32 ws_size; //16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK) -+ u32 ws_size; //16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK) - u32 mclk_rate; //mclk frequency divide by fs (128fs, 192fs, 256fs, 384fs, 512fs, 768fs) -- u32 lrc_pol; //LRC clock polarity (0: normal ,1: inverted) -+ u32 lrc_pol; //LRC clock polarity (0: normal ,1: inverted) - u32 bclk_pol; //BCLK polarity (0: normal, 1: inverted) -- u32 pcm_txtype; //PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) -- u32 pcm_rxtype; //PCM receiver type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) -- u32 pcm_sw; //PCM slot width (8: 8 bits, 16: 16 bits) -- u32 pcm_sync_period;//PCM sync period (16/32/64/128/256) -- u32 pcm_sync_type; //PCM sync symbol size (0: short sync, 1: long sync) -- u32 pcm_start_slot;//PCM start slot index (1--4) -- u32 pcm_lsb_first; //0: MSB first, 1: LSB first -- u32 pcm_ch_num; //PCM channel number (1: one channel, 2: two channel) -+ u32 pcm_txtype; //PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) -+ u32 pcm_rxtype; //PCM receiver type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law) -+ u32 pcm_sw; //PCM slot width (8: 8 bits, 16: 16 bits) -+ u32 pcm_sync_period; //PCM sync period (16/32/64/128/256) -+ u32 pcm_sync_type; //PCM sync symbol size (0: short sync, 1: long sync) -+ u32 pcm_start_slot; //PCM start slot index (1--4) -+ u32 pcm_lsb_first; //0: MSB first, 1: LSB first -+ u32 pcm_ch_num; //PCM channel number (1: one channel, 2: two channel) - - }; - ---- a/sound/soc/sunxi/i2s/sunxi-i2sdma.c -+++ b/sound/soc/sunxi/i2s/sunxi-i2sdma.c -@@ -32,21 +32,32 @@ - #include "sunxi-i2s.h" - #include "sunxi-i2sdma.h" - --static volatile unsigned int dmasrc = 0; --static volatile unsigned int dmadst = 0; -- --//DMA data width --static unsigned int dma_width = 16; -- -+static const struct snd_pcm_hardware sunxi_pcm_out_hardware = { -+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | -+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, -+ .rates = SNDRV_PCM_RATE_8000_384000 | SNDRV_PCM_RATE_KNOT, -+ .rate_min = 8000, -+ .rate_max = 384000, -+ .channels_min = 1, -+ .channels_max = 2, -+ .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ -+ .period_bytes_min = 1024*4,//1024*4, -+ .period_bytes_max = 1024*32,//1024*32, -+ .periods_min = 4,//4, -+ .periods_max = 8,//8, -+ .fifo_size = 128, -+}; - --static const struct snd_pcm_hardware sunxi_pcm_hardware = { -+static const struct snd_pcm_hardware sunxi_pcm_in_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, -- .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, -+ .rates = SNDRV_PCM_RATE_8000_384000 | SNDRV_PCM_RATE_KNOT, - .rate_min = 8000, -- .rate_max = 192000, -+ .rate_max = 384000, - .channels_min = 1, - .channels_max = 2, - .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ -@@ -54,7 +65,7 @@ static const struct snd_pcm_hardware sunxi_pcm_hardware = { - .period_bytes_max = 1024*32,//1024*32, - .periods_min = 4,//4, - .periods_max = 8,//8, -- .fifo_size = 128,//32, -+ .fifo_size = 64, - }; - - struct sunxi_runtime_data { -@@ -67,32 +78,32 @@ struct sunxi_runtime_data { - dma_addr_t dma_pos; - dma_addr_t dma_end; - struct sunxi_dma_params *params; -+ /*DMA data width*/ -+ unsigned int dma_width; - }; - - static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) - { -+ int ret = 0; - struct sunxi_runtime_data *prtd = substream->runtime->private_data; - dma_addr_t pos = prtd->dma_pos; -- unsigned int limit; -- int ret; -- - unsigned long len = prtd->dma_period; -- limit = prtd->dma_limit; -- while(prtd->dma_loaded < limit) { -- if((pos + len) > prtd->dma_end) { -+ unsigned int limit = prtd->dma_limit; -+ int read = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1; -+ -+ while(prtd->dma_loaded < limit){ -+ if((pos + len) > prtd->dma_end){ - len = prtd->dma_end - pos; - } -- -- ret = sunxi_dma_enqueue(prtd->params, pos, len, 0); -- if(ret == 0) { -+ ret = sunxi_dma_enqueue(prtd->params, pos, len, read); -+ if(ret == 0){ - prtd->dma_loaded++; - pos += prtd->dma_period; - if(pos >= prtd->dma_end) - pos = prtd->dma_start; -- }else { -+ }else{ - break; - } -- - } - prtd->dma_pos = pos; - } -@@ -129,31 +140,32 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, - if (!dma) - return 0; - -+ prtd->dma_width = 16; - /* set DMA width for using in sunxi_pcm_prepare*/ - switch(params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: -- dma_width = 16; -+ prtd->dma_width = 16; - break; - case SNDRV_PCM_FORMAT_S20_3LE: -- dma_width = 32; -+ prtd->dma_width = 32; - break; - case SNDRV_PCM_FORMAT_S24_LE: -- dma_width = 32; -+ prtd->dma_width = 32; - break; - } -- /*printk("[IIS-0] sunxi_pcm_hw_params: dma width %d bit\n", dma_width);*/ -- - if (prtd->params == NULL) { - prtd->params = dma; - ret = sunxi_dma_request(prtd->params, 0); - if (ret < 0) { -- return ret; -+ printk("[IIS-0] sunxi_dma_request failed! ret == %d\n", ret); -+ return ret; - } - } - - if (sunxi_dma_set_callback(prtd->params, sunxi_audio_buffdone, - substream) != 0) { - sunxi_dma_release(prtd->params); -+ printk("[IIS-0] sunxi_dma_set_callback failed! ret == %d\n", ret); - prtd->params = NULL; - return -EINVAL; - } -@@ -169,6 +181,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, - prtd->dma_start = runtime->dma_addr; - prtd->dma_pos = prtd->dma_start; - prtd->dma_end = prtd->dma_start + totbytes; -+ - spin_unlock_irq(&prtd->lock); - return 0; - } -@@ -215,8 +228,8 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) - dma_config_t codec_dma_conf; - memset(&codec_dma_conf, 0, sizeof(codec_dma_conf)); - -- /*printk("[IIS-0] sunxi_pcm_prepare: DMA data width=(%d)\n", dma_width);*/ -- if(dma_width > 16) -+ printk("[IIS-0] sunxi_pcm_prepare: playback DMA data width=(%d)\n", prtd->dma_width); -+ if(prtd->dma_width > 16) - { - codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; - codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; -@@ -226,16 +239,42 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) - codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; - codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; - } -- codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_1; --// codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; /*like SPDIF module?*/ -- codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_1; --// codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; /*like SPDIF module?*/ -+ codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; -+ codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; - codec_dma_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT; - codec_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE; - codec_dma_conf.src_drq_type = N_SRC_SDRAM; - codec_dma_conf.dst_drq_type = N_DST_IIS0_TX; - codec_dma_conf.bconti_mode = false; -- codec_dma_conf.irq_spt = CHAN_IRQ_FD; -+ codec_dma_conf.irq_spt = CHAN_IRQ_FD; //buf full done irq -+#endif -+ ret = sunxi_dma_config(prtd->params, &codec_dma_conf, 0); -+ } -+ else { -+#if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I -+#else -+ dma_config_t codec_dma_conf; -+ memset(&codec_dma_conf, 0, sizeof(codec_dma_conf)); -+ -+ printk("[IIS-0] sunxi_pcm_prepare: capture DMA data width=(%d)\n", prtd->dma_width); -+ if(prtd->dma_width > 16) -+ { -+ codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; -+ codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; -+ } -+ else -+ { -+ codec_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; -+ codec_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; -+ } -+ codec_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; -+ codec_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; -+ codec_dma_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE; -+ codec_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT; -+ codec_dma_conf.src_drq_type = N_SRC_IIS0_RX; -+ codec_dma_conf.dst_drq_type = N_DST_SDRAM; -+ codec_dma_conf.bconti_mode = false; -+ codec_dma_conf.irq_spt = CHAN_IRQ_FD; //buf full done irq - #endif - ret = sunxi_dma_config(prtd->params, &codec_dma_conf, 0); - } -@@ -261,15 +300,16 @@ static int sunxi_pcm_trigger(struct snd_pcm_substream *substream, int cmd) - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -- printk("[IIS] dma trigger start\n"); -- printk("[IIS] 0x01c22400+0x24 = %#x, line= %d\n", readl(0xf1c22400+0x24), __LINE__); -+ printk("[IIS-0] dma trigger start\n"); -+ prtd->state |= ST_RUNNING; - sunxi_dma_start(prtd->params); - break; - - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- printk("[IIS] dma trigger stop\n"); -+ printk("[IIS-0] dma trigger stop\n"); -+ prtd->state &= ~ST_RUNNING; - sunxi_dma_stop(prtd->params); - break; - -@@ -288,31 +328,42 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream) - struct sunxi_runtime_data *prtd = runtime->private_data; - unsigned long res = 0; - snd_pcm_uframes_t offset = 0; -+ unsigned int dmasrc = 0; -+ unsigned int dmadst = 0; - - spin_lock(&prtd->lock); - sunxi_dma_getcurposition(prtd->params, -- (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst); -+ (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst); - -- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -- res = dmadst - prtd->dma_start; -- else -- { -- offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr); -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ res = dmasrc + prtd->dma_period - prtd->dma_start; - } -+ else { -+ res = dmadst + prtd->dma_period - prtd->dma_start; -+ //res = dmadst - prtd->dma_start; -+ } -+ offset = bytes_to_frames(runtime, res); - spin_unlock(&prtd->lock); - - if(offset >= runtime->buffer_size) - offset = 0; -- return offset; -+ -+ return offset; - } - -+ - static int sunxi_pcm_open(struct snd_pcm_substream *substream) - { - struct snd_pcm_runtime *runtime = substream->runtime; - struct sunxi_runtime_data *prtd; - - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); -- snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_hardware); -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_out_hardware); -+ } -+ else { -+ snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_in_hardware); -+ } - - prtd = kzalloc(sizeof(struct sunxi_runtime_data), GFP_KERNEL); - if (prtd == NULL) -@@ -361,13 +412,19 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) - { - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; -- size_t size = sunxi_pcm_hardware.buffer_bytes_max; -- -+ size_t size = 0; -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ size = sunxi_pcm_out_hardware.buffer_bytes_max; -+ } -+ else { -+ size = sunxi_pcm_in_hardware.buffer_bytes_max; -+ } - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); -+ - if (!buf->area) - return -ENOMEM; - buf->bytes = size; ---- a/sound/soc/sunxi/i2s/sunxi-sndi2s.c -+++ b/sound/soc/sunxi/i2s/sunxi-sndi2s.c -@@ -29,12 +29,6 @@ - - #include "sndi2s.h" - --/* cleaning code --static struct clk *xtal; --static int clk_users; --static DEFINE_MUTEX(clk_lock); --*/ -- - /* slave mode flag*/ - static int sunxi_i2s_slave = 0; - -@@ -50,7 +44,7 @@ static int sunxi_sndi2s_startup(struct snd_pcm_substream *substream) - { - int ret = 0; - #ifdef ENFORCE_RATES -- struct snd_pcm_runtime *runtime = substream->runtime;; -+ struct snd_pcm_runtime *runtime = substream->runtime; - #endif - - if (!ret) { -@@ -67,16 +61,6 @@ static int sunxi_sndi2s_startup(struct snd_pcm_substream *substream) - - static void sunxi_sndi2s_shutdown(struct snd_pcm_substream *substream) - { --/* cleaning code -- mutex_lock(&clk_lock); -- clk_users -= 1; -- if (clk_users == 0) { -- clk_put(xtal); -- xtal = NULL; -- -- } -- mutex_unlock(&clk_lock); --*/ - } - - typedef struct __MCLK_SET_INF -@@ -194,6 +178,11 @@ static __extclk_set_inf EXTCLK_INF[] = - //192k bitrate - { 192000, 128, 0}, - -+ //352.8k bitrate -+ { 352800, 64, 1}, -+ //384 bitrate -+ { 384000, 64, 0}, -+ - //end flag 0xffffffff - {0xffffffff, 0, 0} - }; -@@ -253,33 +242,13 @@ static int sunxi_sndi2s_hw_params(struct snd_pcm_substream *substream, - int ret = 0; - unsigned long rate = params_rate(params); - u32 mclk_div=0, mpll=0, bclk_div=0, mult_fs=0; --/* -- printk("[IIS-0] sunxi_sndi2s_hw_params: codec_dai=(%s), cpu_dai=(%s)\n", codec_dai->name, cpu_dai->name); -- printk("[IIS-0] sunxi_sndi2s_hw_params: channel num=(%d)\n", params_channels(params)); -- printk("[IIS-0] sunxi_sndi2s_hw_params: sample rate=(%lu)\n", rate); - -- switch (params_format(params)) -- { -- case SNDRV_PCM_FORMAT_S16_LE: -- printk("[IIS-0] sunxi_sndi2s_hw_params: format 16 bit\n"); -- break; -- case SNDRV_PCM_FORMAT_S20_3LE: -- printk("[IIS-0] sunxi_sndi2s_hw_params: format 20 bit in 3 bytes\n"); -- break; -- case SNDRV_PCM_FORMAT_S24_LE: -- printk("[IIS-0] sunxi_sndi2s_hw_params: format 24 bit in 4 bytes\n"); -- break; -- default: -- printk("[IIS-0] sunxi_sndi2s_hw_params: Unsupported format (%d)\n", (int)params_format(params)); -- //return -EINVAL; -- } --*/ - if(!sunxi_i2s_slave) { -- get_clock_divder_master(rate, 32, &mclk_div, &mpll, &bclk_div, &mult_fs); -+ get_clock_divder_master(rate, /*fixed sample width*/32, &mclk_div, &mpll, &bclk_div, &mult_fs); - printk("[IIS-0] get_clock_divder_master: rate=(%lu), mclk_div=(%d), mpll=(%d), bclk_div=(%d), mult_fs=(%d)\n", - rate, mclk_div, mpll, bclk_div, mult_fs); - } else { -- get_clock_divder_slave(rate, 32, &bclk_div, &mpll, &mult_fs); -+ get_clock_divder_slave(rate, /*fixed sample width*/32, &bclk_div, &mpll, &mult_fs); - printk("[IIS-0] get_clock_divder_slave: rate=(%lu), bclk_div=(%d), mpll=(%d), mult_fs=(%d)\n", - rate, bclk_div, mpll, mult_fs); - } -@@ -383,7 +352,7 @@ static int __init sunxi_sndi2s_init(void) - { - int ret, i2s_used = 0, i2s_slave = 0; - -- printk("[IIS]Entered %s\n", __func__); -+ printk("[I2S-0] Entered %s\n", __func__); - - ret = script_parser_fetch("i2s_para", "i2s_used", &i2s_used, 1); - if (ret != 0 || !i2s_used) ---- a/sound/soc/sunxi/spdif/sndspdif.c -+++ b/sound/soc/sunxi/spdif/sndspdif.c -@@ -26,7 +26,7 @@ - #include "sndspdif.h" - - #define SNDSPDIF_RATES (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT) --#define SNDSPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) -+#define SNDSPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) - - struct sndspdif_priv { - int sysclk; -@@ -94,9 +94,18 @@ struct snd_soc_dai_driver sndspdif_dai = { - .rates = SNDSPDIF_RATES, - .formats = SNDSPDIF_FORMATS, - }, -+ .capture = { -+ .stream_name = "Capture", -+ .channels_min = 1, -+ .channels_max = 2, -+ .rates = SNDSPDIF_RATES, -+ .formats = SNDSPDIF_FORMATS, -+ }, - /* pcm operations */ - .ops = &sndspdif_dai_ops, -+ //clear start? - .symmetric_rates = 1, -+ //clear end? - }; - EXPORT_SYMBOL(sndspdif_dai); - -@@ -106,7 +115,7 @@ static int sndspdif_soc_probe(struct snd_soc_codec *codec) - - sndspdif = kzalloc(sizeof(struct sndspdif_priv), GFP_KERNEL); - if(sndspdif == NULL){ -- printk("%s,%d\n",__func__,__LINE__); -+ pr_err("[SPDIF] try to alloc sndspdif failed %s,%d\n", __func__,__LINE__); - return -ENOMEM; - } - snd_soc_codec_set_drvdata(codec, sndspdif); -@@ -161,22 +170,27 @@ static int __init sndspdif_codec_init(void) - int ret, spdif_used = 0; - - ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1); -- if (ret != 0 || !spdif_used) -+ if (ret != 0 || !spdif_used) { -+ printk("[SPDIF] [spdif_para] isn't defined or spdif_used=0\n"); - return -ENODEV; -+ } - - spd_gpio_hdle = gpio_request_ex("spdif_para", "spdif_dout"); - if (0 == spd_gpio_hdle) { -- pr_err("try to request spdif_para gpio failed\n"); -+ pr_err("[SPDIF] try to request spdif_para gpio failed %s,%d\n", __func__,__LINE__); - return -1; - } - - ret = platform_device_register(&sndspdif_codec_device); -- if (ret < 0) -+ if (ret < 0) { -+ pr_err("[SPDIF] try to SPDIF platform_device_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); - return ret; -+ } - - ret = platform_driver_register(&sndspdif_codec_driver); - if (ret < 0) { - platform_device_unregister(&sndspdif_codec_device); -+ pr_err("[SPDIF] try to SPDIF platform_driver_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); - return ret; - } - - ---- a/sound/soc/sunxi/spdif/sunxi_sndspdif.c -+++ b/sound/soc/sunxi/spdif/sunxi_sndspdif.c -@@ -45,7 +45,7 @@ static int sunxi_sndspdif_startup(struct snd_pcm_substream *substream) - { - int ret = 0; - #ifdef ENFORCE_RATES -- struct snd_pcm_runtime *runtime = substream->runtime;; -+ struct snd_pcm_runtime *runtime = substream->runtime; - #endif - if (!ret) { - #ifdef ENFORCE_RATES -@@ -71,7 +71,7 @@ static void sunxi_sndspdif_shutdown(struct snd_pcm_substream *substream) - typedef struct __MCLK_SET_INF - { - __u32 samp_rate; // sample rate -- __u16 mult_fs; // multiply of smaple rate -+ __u16 mult_fs; // multiply of smaple rate - - __u8 clk_div; // mpll division - __u8 mpll; // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz -@@ -83,7 +83,7 @@ typedef struct __BCLK_SET_INF - { - __u8 bitpersamp; // bits per sample - __u8 clk_div; // clock division -- __u16 mult_fs; // multiplay of sample rate -+ __u16 mult_fs; // multiply of sample rate - - } __bclk_set_inf; - -@@ -218,13 +218,13 @@ static struct snd_soc_ops sunxi_sndspdif_ops = { - }; - - static struct snd_soc_dai_link sunxi_sndspdif_dai_link = { -- .name = "SPDIF", -+ .name = "SPDIF", - .stream_name = "SUNXI-SPDIF", - .cpu_dai_name = "sunxi-spdif.0", - .codec_dai_name = "sndspdif", - .platform_name = "sunxi-spdif-pcm-audio.0", - .codec_name = "sunxi-spdif-codec.0", -- .ops = &sunxi_sndspdif_ops, -+ .ops = &sunxi_sndspdif_ops, - }; - - static struct snd_soc_card snd_soc_sunxi_sndspdif = { -@@ -236,6 +236,12 @@ static struct snd_soc_card snd_soc_sunxi_sndspdif = { - - static int __devinit sunxi_sndspdif_probe(struct platform_device *pdev) - { -+ int ret, spdif_used = 0; -+ -+ ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1); -+ if (ret != 0 || !spdif_used) -+ return -ENODEV; -+ - snd_soc_sunxi_sndspdif.dev = &pdev->dev; - return snd_soc_register_card(&snd_soc_sunxi_sndspdif); - } - ---- a/sound/soc/sunxi/spdif/sunxi_spdif.c -+++ b/sound/soc/sunxi/spdif/sunxi_spdif.c -@@ -37,7 +37,7 @@ - #include "sunxi_spdma.h" - #include "sunxi_spdif.h" - --static int regsave[6]; -+static int regsave[9]; - - static struct sunxi_dma_params sunxi_spdif_stereo_out = { - .client.name = "SPDIF out", -@@ -48,9 +48,9 @@ static struct sunxi_dma_params sunxi_spdif_stereo_out = { - }; - - static struct sunxi_dma_params sunxi_spdif_stereo_in = { -- .client.name = "SPDIF out", -+ .client.name = "SPDIF in", - #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I -- .channel = DMACH_NSPDIF, -+ .channel = DMACH_NSPDIF, //??? - #endif - .dma_addr = SUNXI_SPDIFBASE + SUNXI_SPDIF_RXFIFO, - }; -@@ -63,34 +63,33 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on) - { - u32 reg_val; - -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - if (substream->runtime->channels == 1) { -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - reg_val |= SUNXI_SPDIF_TXCFG_SINGLEMOD; -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - } -+ else { -+ reg_val &= ~SUNXI_SPDIF_TXCFG_SINGLEMOD; -+ } -+ reg_val |= SUNXI_SPDIF_TXCFG_ASS; //Sending the last audio (may be 0?) -+ reg_val |= SUNXI_SPDIF_TXCFG_CHSTMODE; //Channel status A&B generated form TX_CHSTA -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - -- //soft reset SPDIF -- writel(0x1, sunxi_spdif.regs + SUNXI_SPDIF_CTL); -- -- //MCLK OUTPUT enable -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -- reg_val |= SUNXI_SPDIF_CTL_MCLKOUTEN; -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); -- -- //flush TX FIFO -+ /*flush TX FIFO and set FIFO empty trigger level*/ - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -- reg_val |= SUNXI_SPDIF_FCTL_FTX; -+ reg_val |= SUNXI_SPDIF_FCTL_TXTL(0x10); //TX FIFO empty Trigger Level -+ reg_val |= SUNXI_SPDIF_FCTL_FTX; //flush TX FIFO - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); - -- //clear interrupt status -+ /*clear interrupt status*/ - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_ISTA); -+ reg_val |= SUNXI_SPDIF_ISTA_TXCLR; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_ISTA); - -- //clear TX counter -+ /*clear TX counter*/ - writel(0, sunxi_spdif.regs + SUNXI_SPDIF_TXCNT); - - if (on) { -- //SPDIF TX ENBALE -+ //SPDIF TX ENABLE - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - reg_val |= SUNXI_SPDIF_TXCFG_TXEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -@@ -99,13 +98,8 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on) - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); - reg_val |= SUNXI_SPDIF_INT_TXDRQEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); -- -- //global enable -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -- reg_val |= SUNXI_SPDIF_CTL_GEN; -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - } else { -- //SPDIF TX DISABALE -+ //SPDIF TX DISABLE - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - reg_val &= ~SUNXI_SPDIF_TXCFG_TXEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -@@ -114,16 +108,48 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on) - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); - reg_val &= ~SUNXI_SPDIF_INT_TXDRQEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); -- -- //global disable -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -- reg_val &= ~SUNXI_SPDIF_CTL_GEN; -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - } - } - --void sunxi_snd_rxctrl(int on) -+void sunxi_snd_rxctrl(struct snd_pcm_substream *substream, int on) - { -+ u32 reg_val; -+ -+ /*flush RX FIFO and set FIFO empty trigger level*/ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ reg_val |= SUNXI_SPDIF_FCTL_RXTL(0x0F); //RX FIFO Trigger Level -+ reg_val |= SUNXI_SPDIF_FCTL_FRX; //flush RX FIFO -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ -+ /*clear interrupt status*/ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_ISTA); -+ reg_val |= SUNXI_SPDIF_ISTA_RXCLR; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_ISTA); -+ -+ /*clear RX counter*/ -+ writel(0, sunxi_spdif.regs + SUNXI_SPDIF_RXCNT); -+ -+ if (on) { -+ /*SPDIF RX ENABLE*/ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); -+ reg_val |= SUNXI_SPDIF_RXCFG_RXEN; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); -+ -+ /*DRQ ENABLE*/ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); -+ reg_val |= SUNXI_SPDIF_INT_RXDRQEN; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); -+ } else { -+ /*SPDIF TX DISABLE*/ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); -+ reg_val &= ~SUNXI_SPDIF_RXCFG_RXEN; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); -+ -+ /*DRQ DISABLE*/ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); -+ reg_val &= ~SUNXI_SPDIF_INT_RXDRQEN; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT); -+ } - } - - static inline int sunxi_snd_is_clkmaster(void) -@@ -135,63 +161,122 @@ static int sunxi_spdif_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) - { - u32 reg_val; - -- reg_val = 0; -- reg_val &= ~SUNXI_SPDIF_TXCFG_SINGLEMOD; -- reg_val |= SUNXI_SPDIF_TXCFG_ASS; -- reg_val &= ~SUNXI_SPDIF_TXCFG_NONAUDIO; -- reg_val |= SUNXI_SPDIF_TXCFG_FMT16BIT; -- reg_val |= SUNXI_SPDIF_TXCFG_CHSTMODE; -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -- -- reg_val = 0; -- reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC; -- reg_val |= SUNXI_SPDIF_FCTL_TXTL(16); -- reg_val |= SUNXI_SPDIF_FCTL_RXTL(15); -- reg_val |= SUNXI_SPDIF_FCTL_TXIM(1); -- reg_val |= SUNXI_SPDIF_FCTL_RXOM(3); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -- - if (!fmt) {//PCM -- reg_val = 0; -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -+ reg_val &= ~SUNXI_SPDIF_TXCFG_NONAUDIO; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -+ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_CHNUM(2)); -+ reg_val &= ~SUNXI_SPDIF_TXCHSTA0_AUDIO; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - -- reg_val = 0; -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); -+ reg_val |= (SUNXI_SPDIF_RXCHSTA0_CHNUM(2)); -+ reg_val &= ~SUNXI_SPDIF_RXCHSTA0_AUDIO; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); - } else { //non PCM - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - reg_val |= SUNXI_SPDIF_TXCFG_NONAUDIO; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - -- reg_val = 0; -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - reg_val |= (SUNXI_SPDIF_TXCHSTA0_CHNUM(2)); - reg_val |= SUNXI_SPDIF_TXCHSTA0_AUDIO; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - -- reg_val = 0; -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); -+ reg_val |= (SUNXI_SPDIF_RXCHSTA0_CHNUM(2)); -+ reg_val |= SUNXI_SPDIF_RXCHSTA0_AUDIO; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); - } - - return 0; - } - - static int sunxi_spdif_hw_params(struct snd_pcm_substream *substream, -- struct snd_pcm_hw_params *params, -- struct snd_soc_dai *dai) -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) - { - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct sunxi_dma_params *dma_data; -+ u32 reg_val, reg_val1; -+ int format; -+ switch (params_format(params)) -+ { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ format = 16; -+ break; -+ case SNDRV_PCM_FORMAT_S20_3LE: -+ format = 20; -+ break; -+ case SNDRV_PCM_FORMAT_S24_LE: -+ format = 24; -+ break; -+ case SNDRV_PCM_FORMAT_S32_LE: -+ format = 24; -+ break; -+ default: -+ return -EINVAL; -+ } - -- /* play or record */ -- if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) -+ /* playback or capture */ -+ if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_data = &sunxi_spdif_stereo_out; -- else -+ -+ reg_val1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -+ reg_val &= ~SUNXI_SPDIF_TXCFG_FMTRVD; -+ reg_val1 &= ~SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN; -+ reg_val1 &= ~(SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(7)); -+ if(format == 16) { -+ reg_val |= SUNXI_SPDIF_TXCFG_FMT16BIT; -+ reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); -+ } -+ else if(format == 20) { -+ reg_val |= SUNXI_SPDIF_TXCFG_FMT20BIT; -+ reg_val1 |= SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN; -+ reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1)); -+ } -+ else { -+ reg_val |= SUNXI_SPDIF_TXCFG_FMT24BIT; -+ reg_val1 |= SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN; -+ reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(5)); -+ } -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -+ writel(reg_val1, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ -+ /* Set TX FIFO Input Mode */ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ reg_val &= ~SUNXI_SPDIF_FCTL_TXIM1; //0. Valid data at the MSB of OWA_TXFIFO register -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ } -+ else { - dma_data = &sunxi_spdif_stereo_in; -+ /* Set TX FIFO Input Mode */ -+ reg_val1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ reg_val &= ~SUNXI_SPDIF_FCTL_RXOM3; -+ reg_val1 &= ~SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN; -+ reg_val1 &= ~(SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(7)); -+ -+ if(format == 16) { -+ reg_val |= SUNXI_SPDIF_FCTL_RXOM3; -+ reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(1)); -+ } -+ else if(format == 20) { -+ reg_val1 |= SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN; -+ reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(1)); -+ } -+ else { -+ reg_val1 |= SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN; -+ reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(5)); -+ } - -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ writel(reg_val1, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); -+ } - snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data); -- - return 0; - } - -@@ -208,7 +293,7 @@ static int sunxi_spdif_trigger(struct snd_pcm_substream *substream, - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { -- sunxi_snd_rxctrl(1); -+ sunxi_snd_rxctrl(substream, 1); - } else { - sunxi_snd_txctrl(substream, 1); - } -@@ -218,9 +303,9 @@ static int sunxi_spdif_trigger(struct snd_pcm_substream *substream, - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { -- sunxi_snd_rxctrl(0); -+ sunxi_snd_rxctrl(substream, 0); - } else { -- sunxi_snd_txctrl(substream, 0); -+ sunxi_snd_txctrl(substream, 0); - } - break; - default: -@@ -246,23 +331,31 @@ static int sunxi_spdif_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id, - - static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div) - { -- u32 reg_val = 0; -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val &= ~(SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xf)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val &= ~(SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xf)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ u32 reg_val_txchsta0 = 0; -+ u32 reg_val_txchsta1 = 0; -+ u32 reg_val_rxchsta0 = 0; -+ u32 reg_val_rxchsta1 = 0; -+ u32 reg_val_txcfg = 0; - - switch(div_id) { - case SUNXI_DIV_MCLK: - { -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -- reg_val &= ~SUNXI_SPDIF_TXCFG_TXRATIO(0x1F); -- reg_val |= SUNXI_SPDIF_TXCFG_TXRATIO(div-1); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -+ reg_val_txchsta0 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -+ reg_val_txchsta0 &= ~(SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xf)); -+ -+ reg_val_txchsta1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta1 &= ~(SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xf)); -+ -+ reg_val_rxchsta0 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); -+ reg_val_rxchsta0 &= ~(SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xf)); -+ -+ reg_val_rxchsta1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); -+ reg_val_rxchsta1 &= ~(SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xf)); -+ -+ reg_val_txcfg = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -+ reg_val_txcfg &= ~SUNXI_SPDIF_TXCFG_TXRATIO(0x1F); -+ reg_val_txcfg |= SUNXI_SPDIF_TXCFG_TXRATIO(div-1); -+ writel(reg_val_txcfg, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); - - if(clk_get_rate(spdif_pll2clk) == 24576000) - { -@@ -270,67 +363,49 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d - { - //24KHZ - case 8: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x6)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x9)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x6)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x9)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x6)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x9)); - break; - - //32KHZ - case 6: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x3)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xC)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x3)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xC)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x3)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xC)); - break; - - //48KHZ - case 4: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x2)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xD)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x2)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xD)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x2)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xD)); - break; - - //96KHZ - case 2: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xA)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x5)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xA)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x5)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xA)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x5)); - break; - - //192KHZ - case 1: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xE)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x1)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xE)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x1)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xE)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x1)); - break; - - default: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(1)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0)); - break; - } - }else{ //22.5792MHz -@@ -338,59 +413,50 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d - { - //22.05khz - case 8: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x4)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xb)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x4)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xb)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x4)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xb)); - break; - - //44.1KHZ - case 4: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x0)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xF)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x0)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xF)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x0)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xF)); - break; - - //88.2khz - case 2: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x8)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x7)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x8)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x7)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x8)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x7)); - break; - - //176.4KHZ - case 1: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xC)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x3)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xC)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x3)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xC)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x3)); - break; - - default: -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -- -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -- reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1)); -+ reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0)); -+ reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(1)); -+ reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0)); -+ - break; - } - } -+ writel(reg_val_txchsta0, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); -+ writel(reg_val_txchsta1, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ -+ writel(reg_val_rxchsta0, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); -+ writel(reg_val_rxchsta1, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); - } - break; - case SUNXI_DIV_BCLK: -@@ -399,7 +465,6 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d - default: - return -EINVAL; - } -- - return 0; - } - -@@ -422,10 +487,13 @@ static void spdifregsave(void) - { - regsave[0] = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); - regsave[1] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG); -- regsave[2] = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL) | (0x3<<16); -+ regsave[2] = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL) | (0x3<<16); //clear TX, RX fifo - regsave[3] = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT); - regsave[4] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - regsave[5] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ regsave[6] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); -+ regsave[7] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); -+ regsave[8] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); - } - - static void spdifregrestore(void) -@@ -436,14 +504,18 @@ static void spdifregrestore(void) - writel(regsave[3], sunxi_spdif.regs + SUNXI_SPDIF_INT); - writel(regsave[4], sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0); - writel(regsave[5], sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1); -+ writel(regsave[6], sunxi_spdif.regs + SUNXI_SPDIF_RXCFG); -+ writel(regsave[7], sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0); -+ writel(regsave[8], sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1); - } - - //#ifdef CONFIG_PM - static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai) - { - u32 reg_val; -- printk("[SPDIF]Enter %s\n", __func__); -+ printk("[SPDIF] Enter %s\n", __func__); - -+ //global disable - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); - reg_val &= ~SUNXI_SPDIF_CTL_GEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); -@@ -453,7 +525,9 @@ static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai) - //disable the module clock - clk_disable(spdif_moduleclk); - -+ //clear start? - clk_disable(spdif_apbclk); -+ //clear end? - - printk("[SPDIF]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); - printk("[SPDIF]SPECIAL CLK 0x01c200C0 = %#x, line= %d\n", *(volatile int*)0xF1C200C0, __LINE__); -@@ -464,9 +538,9 @@ static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai) - static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai) - { - u32 reg_val; -- printk("[SPDIF]Enter %s\n", __func__); -+ printk("[SPDIF] Enter %s\n", __func__); - -- //disable the module clock -+ //enable the module clock - clk_enable(spdif_apbclk); - - //enable the module clock -@@ -474,11 +548,18 @@ static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai) - - spdifregrestore(); - -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC; //set TX FIFO source select as APB bus -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ - reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -+ //soft reset SPDIF -+ reg_val |= SUNXI_SPDIF_CTL_RESET; -+ //global enable - reg_val |= SUNXI_SPDIF_CTL_GEN; - writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - -- //printk("[SPDIF]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); -+ printk("[SPDIF]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008); - printk("[SPDIF]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__); - printk("[SPDIF]SPECIAL CLK 0x01c200C0 = %#x, line = %d\n", *(volatile int*)0xF1C200C0, __LINE__); - -@@ -487,11 +568,11 @@ static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai) - - #define SUNXI_SPDIF_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT) - static struct snd_soc_dai_ops sunxi_spdif_dai_ops = { -- .trigger = sunxi_spdif_trigger, -+ .trigger = sunxi_spdif_trigger, - .hw_params = sunxi_spdif_hw_params, -- .set_fmt = sunxi_spdif_set_fmt, -- .set_clkdiv = sunxi_spdif_set_clkdiv, -- .set_sysclk = sunxi_spdif_set_sysclk, -+ .set_fmt = sunxi_spdif_set_fmt, -+ .set_clkdiv = sunxi_spdif_set_clkdiv, -+ .set_sysclk = sunxi_spdif_set_sysclk, - }; - static struct snd_soc_dai_driver sunxi_spdif_dai = { - .probe = sunxi_spdif_dai_probe, -@@ -502,14 +583,14 @@ static struct snd_soc_dai_driver sunxi_spdif_dai = { - .channels_min = 1, - .channels_max = 2, - .rates = SUNXI_SPDIF_RATES, -- .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = SUNXI_SPDIF_RATES, -- .formats = SNDRV_PCM_FMTBIT_S16_LE,}, -- .symmetric_rates = 1, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,}, - .ops = &sunxi_spdif_dai_ops, -+ .symmetric_rates = 1, - }; - - static int __devinit sunxi_spdif_dev_probe(struct platform_device *pdev) -@@ -521,46 +602,58 @@ static int __devinit sunxi_spdif_dev_probe(struct platform_device *pdev) - if(sunxi_spdif.regs == NULL) - return -ENXIO; - -- //spdif apbclk -- spdif_apbclk = clk_get(NULL, "apb_spdif"); -- if(-1 == clk_enable(spdif_apbclk)){ -- printk("spdif_apbclk failed! line = %d\n", __LINE__); -- } -+ //spdif apbclk -+ spdif_apbclk = clk_get(NULL, "apb_spdif"); -+ if(-1 == clk_enable(spdif_apbclk)){ -+ printk("spdif_apbclk failed! line = %d\n", __LINE__); -+ } - -- spdif_pllx8 = clk_get(NULL, "audio_pllx8"); -+ spdif_pllx8 = clk_get(NULL, "audio_pllx8"); - -- //spdif pll2clk -- spdif_pll2clk = clk_get(NULL, "audio_pll"); -+ //spdif pll2clk -+ spdif_pll2clk = clk_get(NULL, "audio_pll"); - -- //spdif module clk -- spdif_moduleclk = clk_get(NULL, "spdif"); -+ //spdif module clk -+ spdif_moduleclk = clk_get(NULL, "spdif"); - -- if(clk_set_parent(spdif_moduleclk, spdif_pll2clk)){ -- printk("try to set parent of spdif_moduleclk to spdif_pll2ck failed! line = %d\n",__LINE__); -- } -+ if(clk_set_parent(spdif_moduleclk, spdif_pll2clk)){ -+ printk("try to set parent of spdif_moduleclk to spdif_pll2ck failed! line = %d\n",__LINE__); -+ } - -- if(clk_set_rate(spdif_moduleclk, 24576000/8)){ -- printk("set spdif_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); -- } -+ if(clk_set_rate(spdif_moduleclk, 24576000/8)){ -+ printk("set spdif_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__); -+ } - -- if(-1 == clk_enable(spdif_moduleclk)){ -- printk("open spdif_moduleclk failed! line = %d\n", __LINE__); -- } -+ if(-1 == clk_enable(spdif_moduleclk)){ -+ printk("open spdif_moduleclk failed! line = %d\n", __LINE__); -+ } - -- //global enbale -- reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -- reg_val |= SUNXI_SPDIF_CTL_GEN; -- writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC; //set TX FIFO source select as APB bus -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL); -+ -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -+ //soft reset SPDIF -+ reg_val |= SUNXI_SPDIF_CTL_RESET; -+ //global enable -+ reg_val |= SUNXI_SPDIF_CTL_GEN; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); - -- ret = snd_soc_register_dai(&pdev->dev, &sunxi_spdif_dai); -+ ret = snd_soc_register_dai(&pdev->dev, &sunxi_spdif_dai); - -- iounmap(sunxi_spdif.ioregs); -+ iounmap(sunxi_spdif.ioregs); - - return 0; - } - - static int __devexit sunxi_spdif_dev_remove(struct platform_device *pdev) - { -+ int reg_val = 0; -+ //global disable -+ reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL); -+ reg_val &= ~SUNXI_SPDIF_CTL_GEN; -+ writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL); -+ - /* release the module clock */ - clk_disable(spdif_moduleclk); - - ---- a/sound/soc/sunxi/spdif/sunxi_spdif.h -+++ b/sound/soc/sunxi/spdif/sunxi_spdif.h -@@ -17,127 +17,134 @@ - #define SUNXI_SPDIF_H_ - - /*------------------SPDIF register definition--------------------*/ --#define SUNXI_SPDIFBASE 0x01C21000 -+#define SUNXI_SPDIFBASE 0x01C21000 - --#define SUNXI_SPDIF_CTL (0x00) -+#define SUNXI_SPDIF_CTL (0x00) - #define SUNXI_SPDIF_CTL_MCLKDIV(v) ((v)<<4) //v even -- #define SUNXI_SPDIF_CTL_MCLKOUTEN (1<<2) -- #define SUNXI_SPDIF_CTL_GEN (1<<1) -- #define SUNXI_SPDIF_CTL_RESET (1<<0) -+ #define SUNXI_SPDIF_CTL_MCLKOUTEN (1<<2) -+ #define SUNXI_SPDIF_CTL_GEN (1<<1) -+ #define SUNXI_SPDIF_CTL_RESET (1<<0) - --#define SUNXI_SPDIF_TXCFG (0x04) -+#define SUNXI_SPDIF_TXCFG (0x04) - #define SUNXI_SPDIF_TXCFG_SINGLEMOD (1<<31) -- #define SUNXI_SPDIF_TXCFG_ASS (1<<17) -+ #define SUNXI_SPDIF_TXCFG_ASS (1<<17) - #define SUNXI_SPDIF_TXCFG_NONAUDIO (1<<16) -- #define SUNXI_SPDIF_TXCFG_TXRATIO(v) ((v)<<4) -- #define SUNXI_SPDIF_TXCFG_FMTRVD (3<<2) -+ #define SUNXI_SPDIF_TXCFG_TXRATIO(v) ((v)<<4) -+ #define SUNXI_SPDIF_TXCFG_FMTRVD (3<<2) - #define SUNXI_SPDIF_TXCFG_FMT16BIT (0<<2) - #define SUNXI_SPDIF_TXCFG_FMT20BIT (1<<2) - #define SUNXI_SPDIF_TXCFG_FMT24BIT (2<<2) - #define SUNXI_SPDIF_TXCFG_CHSTMODE (1<<1) -- #define SUNXI_SPDIF_TXCFG_TXEN (1<<0) -+ #define SUNXI_SPDIF_TXCFG_TXEN (1<<0) - - #define SUNXI_SPDIF_RXCFG (0x08) - #define SUNXI_SPDIF_RXCFG_LOCKFLAG (1<<4) -- #define SUNXI_SPDIF_RXCFG_CHSTSRC (1<<3) -- #define SUNXI_SPDIF_RXCFG_CHSTCP (1<<1) -- #define SUNXI_SPDIF_RXCFG_RXEN (1<<0) -- --#define SUNXI_SPDIF_TXFIFO (0x0C) -- --#define SUNXI_SPDIF_RXFIFO (0x10) -- --#define SUNXI_SPDIF_FCTL (0x14) -- #define SUNXI_SPDIF_FCTL_FIFOSRC (1<<31) -- #define SUNXI_SPDIF_FCTL_FTX (1<<17) -- #define SUNXI_SPDIF_FCTL_FRX (1<<16) -- #define SUNXI_SPDIF_FCTL_TXTL(v) ((v)<<8) -- #define SUNXI_SPDIF_FCTL_RXTL(v) (((v))<<3) -- #define SUNXI_SPDIF_FCTL_TXIM(v) ((v)<<2) -- #define SUNXI_SPDIF_FCTL_RXOM(v) ((v)<<0) -- --#define SUNXI_SPDIF_FSTA (0x18) -- #define SUNXI_SPDIF_FSTA_TXE (1<<14) -+ #define SUNXI_SPDIF_RXCFG_CHSTSRC (1<<3) -+ #define SUNXI_SPDIF_RXCFG_CHSTCP (1<<1) -+ #define SUNXI_SPDIF_RXCFG_RXEN (1<<0) -+ -+#define SUNXI_SPDIF_TXFIFO (0x0C) -+ -+#define SUNXI_SPDIF_RXFIFO (0x10) -+ -+#define SUNXI_SPDIF_FCTL (0x14) -+ #define SUNXI_SPDIF_FCTL_FIFOSRC (1<<31) -+ #define SUNXI_SPDIF_FCTL_FTX (1<<17) -+ #define SUNXI_SPDIF_FCTL_FRX (1<<16) -+ #define SUNXI_SPDIF_FCTL_TXTL(v) ((v)<<8) -+ #define SUNXI_SPDIF_FCTL_RXTL(v) ((v)<<3) -+ #define SUNXI_SPDIF_FCTL_TXIM0 (0<<2) -+ #define SUNXI_SPDIF_FCTL_TXIM1 (1<<2) -+ #define SUNXI_SPDIF_FCTL_RXOM0 (0<<0) -+ #define SUNXI_SPDIF_FCTL_RXOM1 (1<<0) -+ #define SUNXI_SPDIF_FCTL_RXOM2 (2<<0) -+ #define SUNXI_SPDIF_FCTL_RXOM3 (3<<0) -+ -+#define SUNXI_SPDIF_FSTA (0x18) -+ #define SUNXI_SPDIF_FSTA_TXE (1<<14) - #define SUNXI_SPDIF_FSTA_TXECNTSHT (8) -- #define SUNXI_SPDIF_FSTA_RXA (1<<6) -+ #define SUNXI_SPDIF_FSTA_RXA (1<<6) - #define SUNXI_SPDIF_FSTA_RXACNTSHT (0) - --#define SUNXI_SPDIF_INT (0x1C) -- #define SUNXI_SPDIF_INT_RXLOCKEN (1<<18) -+#define SUNXI_SPDIF_INT (0x1C) -+ #define SUNXI_SPDIF_INT_RXLOCKEN (1<<18) - #define SUNXI_SPDIF_INT_RXUNLOCKEN (1<<17) - #define SUNXI_SPDIF_INT_RXPARERREN (1<<16) -- #define SUNXI_SPDIF_INT_TXDRQEN (1<<7) -- #define SUNXI_SPDIF_INT_TXUIEN (1<<6) -- #define SUNXI_SPDIF_INT_TXOIEN (1<<5) -- #define SUNXI_SPDIF_INT_TXEIEN (1<<4) -- #define SUNXI_SPDIF_INT_RXDRQEN (1<<2) -- #define SUNXI_SPDIF_INT_RXOIEN (1<<1) -- #define SUNXI_SPDIF_INT_RXAIEN (1<<0) -- --#define SUNXI_SPDIF_ISTA (0x20) -+ #define SUNXI_SPDIF_INT_TXDRQEN (1<<7) -+ #define SUNXI_SPDIF_INT_TXUIEN (1<<6) -+ #define SUNXI_SPDIF_INT_TXOIEN (1<<5) -+ #define SUNXI_SPDIF_INT_TXEIEN (1<<4) -+ #define SUNXI_SPDIF_INT_RXDRQEN (1<<2) -+ #define SUNXI_SPDIF_INT_RXOIEN (1<<1) -+ #define SUNXI_SPDIF_INT_RXAIEN (1<<0) -+ -+#define SUNXI_SPDIF_ISTA (0x20) - #define SUNXI_SPDIF_ISTA_RXLOCKSTA (1<<18) -- #define SUNXI_SPDIF_ISTA_RXUNLOCKSTA (1<<17) -- #define SUNXI_SPDIF_ISTA_RXPARERRSTA (1<<16) -- #define SUNXI_SPDIF_ISTA_TXUSTA (1<<6) -- #define SUNXI_SPDIF_ISTA_TXOSTA (1<<5) -- #define SUNXI_SPDIF_ISTA_TXESTA (1<<4) -- #define SUNXI_SPDIF_ISTA_RXOSTA (1<<1) -- #define SUNXI_SPDIF_ISTA_RXASTA (1<<0) -- --#define SUNXI_SPDIF_TXCNT (0x24) -- --#define SUNXI_SPDIF_RXCNT (0x28) -- --#define SUNXI_SPDIF_TXCHSTA0 (0x2C) -- #define SUNXI_SPDIF_TXCHSTA0_CLK(v) ((v)<<28) -- #define SUNXI_SPDIF_TXCHSTA0_SAMFREQ(v) ((v)<<24) -- #define SUNXI_SPDIF_TXCHSTA0_CHNUM(v) ((v)<<20) -- #define SUNXI_SPDIF_TXCHSTA0_SRCNUM(v) ((v)<<16) -- #define SUNXI_SPDIF_TXCHSTA0_CATACOD(v) ((v)<<8) -- #define SUNXI_SPDIF_TXCHSTA0_MODE(v) ((v)<<6) -- #define SUNXI_SPDIF_TXCHSTA0_EMPHASIS(v) ((v)<<3) -- #define SUNXI_SPDIF_TXCHSTA0_CP (1<<2) -- #define SUNXI_SPDIF_TXCHSTA0_AUDIO (1<<1) -- #define SUNXI_SPDIF_TXCHSTA0_PRO (1<<0) -- --#define SUNXI_SPDIF_TXCHSTA1 (0x30) -- #define SUNXI_SPDIF_TXCHSTA1_CGMSA(v) ((v)<<8) -+ #define SUNXI_SPDIF_ISTA_RXUNLOCKSTA (1<<17) -+ #define SUNXI_SPDIF_ISTA_RXPARERRSTA (1<<16) -+ #define SUNXI_SPDIF_ISTA_TXUSTA (1<<6) -+ #define SUNXI_SPDIF_ISTA_TXOSTA (1<<5) -+ #define SUNXI_SPDIF_ISTA_TXESTA (1<<4) -+ #define SUNXI_SPDIF_ISTA_RXOSTA (1<<1) -+ #define SUNXI_SPDIF_ISTA_RXASTA (1<<0) -+ #define SUNXI_SPDIF_ISTA_RXCLR (SUNXI_SPDIF_ISTA_RXLOCKSTA | SUNXI_SPDIF_ISTA_RXUNLOCKSTA | SUNXI_SPDIF_ISTA_RXPARERRSTA | SUNXI_SPDIF_ISTA_RXOSTA | SUNXI_SPDIF_ISTA_RXASTA) -+ #define SUNXI_SPDIF_ISTA_TXCLR (SUNXI_SPDIF_ISTA_TXUSTA | SUNXI_SPDIF_ISTA_TXOSTA | SUNXI_SPDIF_ISTA_TXESTA) -+ -+ -+#define SUNXI_SPDIF_TXCNT (0x24) -+ -+#define SUNXI_SPDIF_RXCNT (0x28) -+ -+#define SUNXI_SPDIF_TXCHSTA0 (0x2C) -+ #define SUNXI_SPDIF_TXCHSTA0_CLK(v) ((v)<<28) -+ #define SUNXI_SPDIF_TXCHSTA0_SAMFREQ(v) ((v)<<24) -+ #define SUNXI_SPDIF_TXCHSTA0_CHNUM(v) ((v)<<20) -+ #define SUNXI_SPDIF_TXCHSTA0_SRCNUM(v) ((v)<<16) -+ #define SUNXI_SPDIF_TXCHSTA0_CATACOD(v) ((v)<<8) -+ #define SUNXI_SPDIF_TXCHSTA0_MODE(v) ((v)<<6) -+ #define SUNXI_SPDIF_TXCHSTA0_EMPHASIS(v) ((v)<<3) -+ #define SUNXI_SPDIF_TXCHSTA0_CP (1<<2) -+ #define SUNXI_SPDIF_TXCHSTA0_AUDIO (1<<1) -+ #define SUNXI_SPDIF_TXCHSTA0_PRO (1<<0) -+ -+#define SUNXI_SPDIF_TXCHSTA1 (0x30) -+ #define SUNXI_SPDIF_TXCHSTA1_CGMSA(v) ((v)<<8) - #define SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(v) ((v)<<4) - #define SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(v) ((v)<<1) -- #define SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN (1<<0) -- --#define SUNXI_SPDIF_RXCHSTA0 (0x34) -- #define SUNXI_SPDIF_RXCHSTA0_CLK(v) ((v)<<28) -- #define SUNXI_SPDIF_RXCHSTA0_SAMFREQ(v) ((v)<<24) -- #define SUNXI_SPDIF_RXCHSTA0_CHNUM(v) ((v)<<20) -- #define SUNXI_SPDIF_RXCHSTA0_SRCNUM(v) ((v)<<16) -- #define SUNXI_SPDIF_RXCHSTA0_CATACOD(v) ((v)<<8) -- #define SUNXI_SPDIF_RXCHSTA0_MODE(v) ((v)<<6) -- #define SUNXI_SPDIF_RXCHSTA0_EMPHASIS(v) ((v)<<3) -- #define SUNXI_SPDIF_RXCHSTA0_CP (1<<2) -- #define SUNXI_SPDIF_RXCHSTA0_AUDIO (1<<1) -- #define SUNXI_SPDIF_RXCHSTA0_PRO (1<<0) -- --#define SUNXI_SPDIF_RXCHSTA1 (0x38) -- #define SUNXI_SPDIF_RXCHSTA1_CGMSA(v) ((v)<<8) -+ #define SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN (1<<0) -+ -+#define SUNXI_SPDIF_RXCHSTA0 (0x34) -+ #define SUNXI_SPDIF_RXCHSTA0_CLK(v) ((v)<<28) -+ #define SUNXI_SPDIF_RXCHSTA0_SAMFREQ(v) ((v)<<24) -+ #define SUNXI_SPDIF_RXCHSTA0_CHNUM(v) ((v)<<20) -+ #define SUNXI_SPDIF_RXCHSTA0_SRCNUM(v) ((v)<<16) -+ #define SUNXI_SPDIF_RXCHSTA0_CATACOD(v) ((v)<<8) -+ #define SUNXI_SPDIF_RXCHSTA0_MODE(v) ((v)<<6) -+ #define SUNXI_SPDIF_RXCHSTA0_EMPHASIS(v) ((v)<<3) -+ #define SUNXI_SPDIF_RXCHSTA0_CP (1<<2) -+ #define SUNXI_SPDIF_RXCHSTA0_AUDIO (1<<1) -+ #define SUNXI_SPDIF_RXCHSTA0_PRO (1<<0) -+ -+#define SUNXI_SPDIF_RXCHSTA1 (0x38) -+ #define SUNXI_SPDIF_RXCHSTA1_CGMSA(v) ((v)<<8) - #define SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(v) ((v)<<4) - #define SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(v) ((v)<<1) -- #define SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN (1<<0) -+ #define SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN (1<<0) - - /*--------------------------------CCM register definition---------------------*/ --#define SUNXI_CCMBASE (0x01C20000) -+#define SUNXI_CCMBASE (0x01C20000) - --#define SUNXI_CCMBASE_AUDIOHOSCPLL (0x08) -- #define SUNXI_CCMBASE_AUDIOHOSCPLL_EN (1<<31) -- #define SUNXI_CCMBASE_AUDIOHOSCPLL_24576M (1<<27) -- #define SUNXI_CCMBASE_AUDIOHOSCPLL_225792M (0<<27) -+#define SUNXI_CCMBASE_AUDIOHOSCPLL (0x08) -+ #define SUNXI_CCMBASE_AUDIOHOSCPLL_EN (1<<31) -+ #define SUNXI_CCMBASE_AUDIOHOSCPLL_24576M (1<<27) -+ #define SUNXI_CCMBASE_AUDIOHOSCPLL_225792M (0<<27) - --#define SUNXI_CCMBASE_APBGATE (0x68) -- #define SUNXI_CCMBASE_APBGATE_SPDIFGATE (1<<1) -+#define SUNXI_CCMBASE_APBGATE (0x68) -+ #define SUNXI_CCMBASE_APBGATE_SPDIFGATE (1<<1) - --#define SUNXI_CCMBASE_AUDIOCLK (0xC0) -+#define SUNXI_CCMBASE_AUDIOCLK (0xC0) - #define SUNXI_CCMBASE_AUDIOCLK_SPDIFSPEGATE (1<<31) -- #define SUNXI_CCMBASE_AUDIOCLK_DIV(v) ((v)<<16) -+ #define SUNXI_CCMBASE_AUDIOCLK_DIV(v) ((v)<<16) - - /* Clock dividers */ - #define SUNXI_DIV_MCLK 0 -@@ -156,6 +163,6 @@ extern struct sunxi_spdif_info sunxi_spdif; - unsigned int sunxi_spdif_get_clockrate(void); - - extern void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on); --extern void sunxi_snd_rxctrl(int on); -+extern void sunxi_snd_rxctrl(struct snd_pcm_substream *substream, int on); - - #endif - ---- a/sound/soc/sunxi/spdif/sunxi_spdma.c -+++ b/sound/soc/sunxi/spdif/sunxi_spdma.c -@@ -34,25 +34,40 @@ - #include "sunxi_spdif.h" - #include "sunxi_spdma.h" - --static volatile unsigned int dmasrc = 0; --static volatile unsigned int dmadst = 0; -+static const struct snd_pcm_hardware sunxi_pcm_out_hardware = { -+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | -+ SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | -+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, -+ .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, -+ .rate_min = 8000, -+ .rate_max = 192000, -+ .channels_min = 1, -+ .channels_max = 2, -+ .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ -+ .period_bytes_min = 1024,//1024*4, -+ .period_bytes_max = 1024*32,//1024*128, -+ .periods_min = 4, -+ .periods_max = 8, -+ .fifo_size = 32, -+}; - --static const struct snd_pcm_hardware sunxi_pcm_hardware = { -+static const struct snd_pcm_hardware sunxi_pcm_in_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, -- .formats = SNDRV_PCM_FMTBIT_S16_LE, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, - .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT, - .rate_min = 8000, - .rate_max = 192000, - .channels_min = 1, - .channels_max = 2, -- .buffer_bytes_max = 128*1024, //1024*1024 /* value must be (2^n)Kbyte size */ -- .period_bytes_min = 1024*4,//1024*4, -+ .buffer_bytes_max = 128*1024, /* value must be (2^n)Kbyte size */ -+ .period_bytes_min = 1024,//1024*4, - .period_bytes_max = 1024*32,//1024*128, -- .periods_min = 4,//8, -- .periods_max = 8,//8, -- .fifo_size = 32,//32, -+ .periods_min = 4, -+ .periods_max = 8, -+ .fifo_size = 32, - }; - - struct sunxi_runtime_data { -@@ -65,6 +80,8 @@ struct sunxi_runtime_data { - dma_addr_t dma_pos; - dma_addr_t dma_end; - struct sunxi_dma_params *params; -+ /*DMA data width*/ -+ unsigned int dma_width; - }; - - static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) -@@ -73,8 +90,8 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) - dma_addr_t pos = prtd->dma_pos; - unsigned int limit; - int ret; -- - unsigned long len = prtd->dma_period; -+ int read = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1; - - limit = prtd->dma_limit; - while(prtd->dma_loaded < limit){ -@@ -82,7 +99,7 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream) - len = prtd->dma_end - pos; - } - -- ret = sunxi_dma_enqueue(prtd->params, pos, len, 0); -+ ret = sunxi_dma_enqueue(prtd->params, pos, len, read); - if (ret == 0) { - prtd->dma_loaded++; - pos += prtd->dma_period; -@@ -120,17 +137,24 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, - struct sunxi_runtime_data *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - unsigned long totbytes = params_buffer_bytes(params); -- struct sunxi_dma_params *dma = -- snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); -+ struct sunxi_dma_params *dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - int ret = 0; - if (!dma) - return 0; - -+ if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) { -+ prtd->dma_width = 16; -+ } -+ else { -+ prtd->dma_width = 32; -+ } -+ - if (prtd->params == NULL) { - prtd->params = dma; - ret = sunxi_dma_request(prtd->params, 0); - if (ret < 0) { -- return ret; -+ pr_err("[SPDIF] sunxi_dma_request failed! (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); -+ return ret; - } - } - -@@ -138,6 +162,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, - substream) != 0) { - sunxi_dma_release(prtd->params); - prtd->params = NULL; -+ pr_err("[SPDIF] sunxi_dma_set_callback failed! (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); - return -EINVAL; - } - -@@ -153,6 +178,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream, - prtd->dma_pos = prtd->dma_start; - prtd->dma_end = prtd->dma_start + totbytes; - spin_unlock_irq(&prtd->lock); -+ - return 0; - } - -@@ -197,16 +223,22 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) - #else - dma_config_t spdif_dma_conf; - memset(&spdif_dma_conf, 0, sizeof(spdif_dma_conf)); -- spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; -+ if(prtd->dma_width > 16) { -+ spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; -+ spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; -+ } -+ else { -+ spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; -+ spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; -+ } - spdif_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; -- spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; - spdif_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; - spdif_dma_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT; - spdif_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE; - spdif_dma_conf.bconti_mode = false; - spdif_dma_conf.irq_spt = CHAN_IRQ_FD; - spdif_dma_conf.src_drq_type = N_SRC_SDRAM; -- spdif_dma_conf.dst_drq_type = N_DST_SPDIF_TX;//DRQDST_SPDIFTX; -+ spdif_dma_conf.dst_drq_type = N_DST_SPDIF_TX; - #endif - ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0); - } else { -@@ -221,10 +253,27 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream) - spdif_dma_conf.hf_irq = SW_DMA_IRQ_FULL|SW_DMA_IRQ_HALF; - spdif_dma_conf.from = prtd->params->dma_addr; - spdif_dma_conf.to = prtd->dma_start; -- ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0); - #else -- return -EINVAL; -+ dma_config_t spdif_dma_conf; -+ memset(&spdif_dma_conf, 0, sizeof(spdif_dma_conf)); -+ if(prtd->dma_width > 16) { -+ spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT; -+ spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT; -+ } -+ else { -+ spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT; -+ spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT; -+ } -+ spdif_dma_conf.xfer_type.src_bst_len = DATA_BRST_4; -+ spdif_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4; -+ spdif_dma_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE; -+ spdif_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT; -+ spdif_dma_conf.bconti_mode = false; -+ spdif_dma_conf.irq_spt = CHAN_IRQ_FD; -+ spdif_dma_conf.src_drq_type = N_SRC_SPDIF_RX; -+ spdif_dma_conf.dst_drq_type = N_DST_SDRAM; - #endif -+ ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0); - } - /* flush the DMA channel */ - prtd->dma_loaded = 0; -@@ -248,12 +297,14 @@ static int sunxi_pcm_trigger(struct snd_pcm_substream *substream, int cmd) - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - sunxi_dma_start(prtd->params); -+ prtd->state |= ST_RUNNING; - break; - - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - sunxi_dma_stop(prtd->params); -+ prtd->state &= ~ST_RUNNING; - break; - - default: -@@ -271,21 +322,26 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream) - struct sunxi_runtime_data *prtd = runtime->private_data; - unsigned long res = 0; - snd_pcm_uframes_t offset = 0; -+ dma_addr_t dmasrc = 0; -+ dma_addr_t dmadst = 0; -+ - - spin_lock(&prtd->lock); - sunxi_dma_getcurposition(prtd->params, - (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst); - -- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE){ -- res = dmadst - prtd->dma_start; -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){ -+ res = dmasrc + prtd->dma_period - prtd->dma_start; - } else { -- offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr); -+ res = dmadst + prtd->dma_period - prtd->dma_start; - } -+ offset = bytes_to_frames(runtime, res); - spin_unlock(&prtd->lock); - - if(offset >= runtime->buffer_size) - offset = 0; -- return offset; -+ -+ return offset; - } - - static int sunxi_pcm_open(struct snd_pcm_substream *substream) -@@ -294,11 +350,17 @@ static int sunxi_pcm_open(struct snd_pcm_substream *substream) - struct sunxi_runtime_data *prtd; - - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); -- snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_hardware); -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_out_hardware); -+ } else { -+ snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_in_hardware); -+ } - - prtd = kzalloc(sizeof(struct sunxi_runtime_data), GFP_KERNEL); -- if (prtd == NULL) -+ if (prtd == NULL) { -+ pr_err("[SPDIF] try to alloc sunxi_runtime_data failed %s,%d\n", __func__,__LINE__); - return -ENOMEM; -+ } - - spin_lock_init(&prtd->lock); - -@@ -311,7 +373,6 @@ static int sunxi_pcm_close(struct snd_pcm_substream *substream) - struct snd_pcm_runtime *runtime = substream->runtime; - struct sunxi_runtime_data *prtd = runtime->private_data; - kfree(prtd); -- - return 0; - } - -@@ -342,8 +403,13 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) - { - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; -- size_t size = sunxi_pcm_hardware.buffer_bytes_max; -+ size_t size = 0; - -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { -+ size = sunxi_pcm_out_hardware.buffer_bytes_max; -+ } else { -+ size = sunxi_pcm_in_hardware.buffer_bytes_max; -+ } - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; -@@ -384,6 +450,7 @@ static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd) - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - -+ - if (!card->dev->dma_mask) - card->dev->dma_mask = &sunxi_pcm_mask; - if (!card->dev->coherent_dma_mask) -@@ -443,16 +510,19 @@ static int __init sunxi_soc_platform_spdif_init(void) - int ret, spdif_used = 0; - - ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1); -- if (ret != 0 || !spdif_used) -+ if (ret != 0 || !spdif_used) - return -ENODEV; - - ret = platform_device_register(&sunxi_spdif_pcm_device); -- if (ret < 0) -+ if (ret < 0) { -+ pr_err("[SPDIF] try to SPDIF platform_device_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); - return ret; -+ } - - ret = platform_driver_register(&sunxi_spdif_pcm_driver); - if (ret < 0) { - platform_device_unregister(&sunxi_spdif_pcm_device); -+ pr_err("[SPDIF] try to SPDIF platform_driver_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__); - return ret; - } - return 0; diff --git a/patch/kernel/sunxi-default/rt8192cu_driver_missing_case_fix.patch b/patch/kernel/sunxi-default/rt8192cu_driver_missing_case_fix.patch deleted file mode 100644 index 64aea3b9c1..0000000000 --- a/patch/kernel/sunxi-default/rt8192cu_driver_missing_case_fix.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c -index 25db369..34ce064 100644 ---- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c -+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c -@@ -1946,6 +1946,14 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) - rtl_write_word(rtlpriv, REG_RXFLTMAP2, *(u16 *)val); - mac->rx_data_filter = *(u16 *)val; - break; -+ case HW_VAR_KEEP_ALIVE:{ -+ u8 array[2]; -+ array[0] = 0xff; -+ array[1] = *((u8 *)val); -+ rtl92c_fill_h2c_cmd(hw, H2C_92C_KEEP_ALIVE_CTRL, 2, -+ array); -+ break; -+ } - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "switch case not processed\n"); - diff --git a/patch/kernel/sunxi-default/sun7i_SPI_driver.patch b/patch/kernel/sunxi-default/sun7i_SPI_driver.patch deleted file mode 100644 index 3fcc586e89..0000000000 --- a/patch/kernel/sunxi-default/sun7i_SPI_driver.patch +++ /dev/null @@ -1,2182 +0,0 @@ -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index c4fb46a..2f9c804 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -381,6 +381,19 @@ config SUN5I_SPI_NDMA - This selects SPI DMA mode with DMA transfer - Y select NDMA mode and N select DDMA mode - -+config SPI_SUN7I -+ tristate "SUN7I SPI Controller" -+ depends on ARCH_SUN7I -+ help -+ Allwinner Soc SPI controller,present on SUN7I chips. -+ -+config SUN7I_SPI_NDMA -+ bool "SUN7I SPI Normal DMA mode select" -+ depends on SPI_SUN7I -+ help -+ This selects SPI DMA mode with DMA transfer -+ Y select NDMA mode and N select DDMA mode -+ - config SPI_TEGRA - tristate "Nvidia Tegra SPI controller" - depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA -diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile -index a13913f..f26f012 100644 ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -64,4 +64,4 @@ obj-$(CONFIG_SPI_TXX9) += spi-txx9.o - obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o - obj-$(CONFIG_SPI_SUN4I) += spi_sunxi.o - obj-$(CONFIG_SPI_SUN5I) += spi_sunxi.o -- -+obj-$(CONFIG_SPI_SUN7I) += spi-sun7i.o -diff --git a/drivers/spi/spi-sun7i.c b/drivers/spi/spi-sun7i.c -new file mode 100644 -index 0000000..a9967b7 ---- /dev/null -+++ b/drivers/spi/spi-sun7i.c -@@ -0,0 +1,2142 @@ -+/* -+ * drivers/spi/spi-sun7i.c -+ * Copyright (C) 2012 - 2016 Reuuimlla Limited -+ * Pan Nan -+ * James Deng -+ * -+ * SUN7I SPI Controller Driver -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define SPI_DEBUG_LEVEL 2 -+ -+#if (SPI_DEBUG_LEVEL == 1) -+ #define spi_dbg(format,args...) do {} while (0) -+ #define spi_inf(format,args...) do {} while (0) -+ #define spi_err(format,args...) printk(KERN_ERR "[spi-err] "format,##args) -+#elif (SPI_DEBUG_LEVEL == 2) -+ #define spi_dbg(format,args...) do {} while (0) -+ #define spi_inf(format,args...) printk(KERN_INFO"[spi-inf] "format,##args) -+ #define spi_err(format,args...) printk(KERN_ERR "[spi-err] "format,##args) -+#elif (SPI_DEBUG_LEVEL == 3) -+ #define spi_dbg(format,args...) printk(KERN_INFO"[spi-dbg] "format,##args) -+ #define spi_inf(format,args...) printk(KERN_INFO"[spi-inf] "format,##args) -+ #define spi_err(format,args...) printk(KERN_ERR "[spi-err] "format,##args) -+#endif -+ -+enum spi_duplex_flag { -+ HALF_DUPLEX_RX, // half duplex read -+ HALF_DUPLEX_TX, // half duplex write -+ FULL_DUPLEX_RX_TX, // full duplex read and write -+ DUPLEX_NULL, -+}; -+ -+enum spi_dma_dir { -+ SPI_DMA_NULL = 0, -+ SPI_DMA_RDEV = 1, -+ SPI_DMA_WDEV = 2, -+}; -+ -+#define SYS_SPI_PIN -+#ifndef SYS_SPI_PIN -+static void* __iomem gpio_addr = NULL; -+ -+/* gpio base address */ -+#define _PIO_BASE_ADDRESS (0x01c20800) -+ -+/* gpio spi1 */ -+#define _Pn_CFG1(n) ( (n)*0x24 + 0x04 + gpio_addr ) -+#define _Pn_DRV1(n) ( (n)*0x24 + 0x14 + gpio_addr ) -+#define _Pn_PUL1(n) ( (n)*0x24 + 0x1C + gpio_addr ) -+#endif -+ -+struct sun7i_spi { -+ struct platform_device *pdev; -+ struct spi_master *master; /* kzalloc */ -+ void __iomem *base_addr; /* register */ -+ struct clk *hclk; /* ahb spi gating bit */ -+ struct clk *mclk; /* ahb spi gating bit */ -+ unsigned long gpio_hdle; -+ dma_chan_type_e dma_type; -+ int dma_id_tx; -+ int dma_id_rx; -+ dma_hdl_t dma_hdle_tx; -+ dma_hdl_t dma_hdle_rx; -+ enum spi_duplex_flag duplex_flag; -+ unsigned int irq; /* irq NO. */ -+ int busy; -+#define SPI_FREE (1<<0) -+#define SPI_SUSPND (1<<1) -+#define SPI_BUSY (1<<2) -+ int result; /* 0: succeed, -1: fail */ -+ struct workqueue_struct *workqueue; -+ struct work_struct work; -+ struct list_head queue; /* spi messages */ -+ spinlock_t lock; -+ struct completion done; /* wakup another spi transfer */ -+ /* keep select during one message */ -+ void (*cs_control)(struct spi_device *spi, bool on); -+ /* (1) enable cs1, cs_bitmap = SPI_CHIP_SELECT_CS1; -+ * (2) enable cs0&cs1,cs_bitmap = SPI_CHIP_SELECT_CS0|SPI_CHIP_SELECT_CS1; -+ */ -+#define SPI_CHIP_SELECT_CS0 (0x01) -+#define SPI_CHIP_SELECT_CS1 (0x02) -+ int cs_bitmap; /* cs0- 0x1, cs1-0x2, cs0 & cs1-0x3. */ -+}; -+ -+/* config chip select */ -+s32 spi_set_cs(u32 chipselect, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ -+ if (chipselect < 4) { -+ reg_val &= ~SPI_CTL_SS_MASK;/* SS-chip select, clear two bits */ -+ reg_val |= chipselect << SPI_SS_BIT_POS;/* set chip select */ -+ -+ writel(reg_val, base_addr + SPI_CTL_REG); -+ -+ return 0; -+ } else { -+ spi_err("%s: chip select set fail, cs = %d\n", __func__, chipselect); -+ return -1; -+ } -+} -+ -+/* select dma type */ -+s32 spi_sel_dma_type(u32 dma_type, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ -+ reg_val &= ~SPI_CTL_DMAMOD; -+ if (dma_type) { -+ reg_val |= 1 << 5; -+ } -+ writel(reg_val, base_addr + SPI_CTL_REG); -+ -+ return 0; -+} -+ -+/* config spi */ -+void spi_config(u32 master, u32 config, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ -+ /* 1. POL */ -+ if (config & SPI_POL_ACTIVE_) { -+ reg_val |= SPI_CTL_POL; -+ } else { -+ reg_val &= ~SPI_CTL_POL; /* default POL = 0 */ -+ } -+ -+ /*2. PHA */ -+ if (config & SPI_PHA_ACTIVE_) { -+ reg_val |= SPI_CTL_PHA; -+ } else { -+ reg_val &= ~SPI_CTL_PHA; /* default PHA = 0 */ -+ } -+ -+ /* 3. SSPOL, chip select signal polarity */ -+ if (config & SPI_CS_HIGH_ACTIVE_) { -+ reg_val &= ~SPI_CTL_SSPOL; -+ } else { -+ reg_val |= SPI_CTL_SSPOL; /* default SSPOL = 1,Low level effective */ -+ } -+ -+ /* 4. LMTF-LSB/MSB transfer first select */ -+ if (config & SPI_LSB_FIRST_ACTIVE_) { -+ reg_val |= SPI_CTL_LMTF; -+ } else { -+ reg_val &= ~SPI_CTL_LMTF; /* default LMTF = 0, MSB first */ -+ } -+ -+ /* master mode: set DDB,DHB,SMC,SSCTL */ -+ if (master == 1) { -+ /* 5. dummy burst type */ -+ if (config & SPI_DUMMY_ONE_ACTIVE_) { -+ reg_val |= SPI_CTL_DDB; -+ } else { -+ reg_val &= ~SPI_CTL_DDB; /* default DDB =0, ZERO */ -+ } -+ -+ /* 6. discard hash burst-DHB */ -+ if (config & SPI_RECEIVE_ALL_ACTIVE_) { -+ reg_val &= ~SPI_CTL_DHB; -+ } else { -+ reg_val |= SPI_CTL_DHB; /* default DHB =1, discard unused burst */ -+ } -+ -+ /* 7. set SMC = 1, SSCTL = 0, TPE = 1 */ -+ reg_val &= ~SPI_CTL_SSCTL; -+ reg_val |= SPI_CTL_T_PAUSE_EN; -+ } else { -+ /* tips for slave mode config */ -+ spi_inf("%s: slave mode configurate control register\n", __func__); -+ } -+ -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* restore reg data after transfer complete */ -+void spi_restore_state(u32 master, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ -+ /* -+ * config spi control register -+ * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | -+ * | DHB| DDB | SS | SMC | XCH |TFRST|RFRST|SSCTL| MSB | TBW |SSPOL| POL | PHA | MOD | EN | -+ * | 1 | 0 | 00 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | -+ */ -+ /* master mode */ -+ if (master) { -+ reg_val |= (SPI_CTL_DHB | SPI_CTL_SSPOL | SPI_CTL_POL | SPI_CTL_PHA | SPI_CTL_FUNC_MODE | SPI_CTL_EN); -+ -+ /* new bit,transmit pause enable,stop smart dummy when rxfifo full */ -+ reg_val |= SPI_CTL_T_PAUSE_EN; -+ -+ /* |SPI_CTL_TBW); //deleted SPI_CTL_TBW bit for aw1623, this bit is defined for dma mode select, 2011-5-26 19:55:32 */ -+ reg_val &= ~(SPI_CTL_DDB | SPI_CTL_SS_MASK | SPI_CTL_XCH | SPI_CTL_SSCTL | SPI_CTL_LMTF); -+ } else { /* slave mode */ -+ reg_val |= (SPI_CTL_SSPOL | SPI_CTL_POL | SPI_CTL_PHA || SPI_CTL_EN); -+ -+ /* |SPI_CTL_TBW); //deleted SPI_CTL_TBW bit for aw1623, this bit is defined for dma mode select, 2011-5-26 19:55:32 */ -+ reg_val &= ~(SPI_CTL_DHB | SPI_CTL_FUNC_MODE | SPI_CTL_DDB | SPI_CTL_SS_MASK | SPI_CTL_XCH | SPI_CTL_SSCTL | SPI_CTL_LMTF); -+ } -+ -+ spi_dbg("control register set default value: 0x%08x\n", reg_val); -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* set spi clock */ -+void spi_set_clk(u32 spi_clk, u32 ahb_clk, void *base_addr) -+{ -+ u32 reg_val = 0; -+ u32 N = 0; -+ u32 div_clk = (ahb_clk >> 1) / spi_clk; -+ -+ reg_val = readl(base_addr + SPI_CLK_RATE_REG); -+ -+ if (div_clk <= SPI_CLK_SCOPE) { -+ if (div_clk != 0) { -+ div_clk--; -+ } -+ -+ reg_val &= ~SPI_CLKCTL_CDR2; -+ reg_val |= (div_clk | SPI_CLKCTL_DRS); -+ } else { -+ while (1) { -+ if (div_clk == 1) { -+ break; -+ } -+ div_clk >>= 1; -+ N++; -+ }; -+ -+ reg_val &= ~(SPI_CLKCTL_CDR1 | SPI_CLKCTL_DRS); -+ reg_val |= (N << 8); -+ } -+ -+ writel(reg_val, base_addr + SPI_CLK_RATE_REG); -+} -+ -+/* start spi transfer */ -+void spi_start_xfer(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val |= SPI_CTL_XCH; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* query tranfer is completed in smc mode */ -+u32 spi_query_xfer(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ return (reg_val & SPI_CTL_XCH); -+} -+ -+/* enable spi bus */ -+void spi_enable_bus(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val |= SPI_CTL_EN; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* disbale spi bus */ -+void spi_disable_bus(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val &= ~SPI_CTL_EN; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* set master mode */ -+void spi_set_master(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val |= SPI_CTL_FUNC_MODE; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* set slave mode */ -+void spi_set_slave(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val &= ~SPI_CTL_FUNC_MODE; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* disable irq type */ -+void spi_disable_irq(u32 bitmap, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_INT_CTL_REG); -+ bitmap &= SPI_INTEN_MASK; -+ reg_val &= ~bitmap; -+ writel(reg_val, base_addr + SPI_INT_CTL_REG); -+} -+ -+/* enable irq type */ -+void spi_enable_irq(u32 bitmap, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_INT_CTL_REG); -+ bitmap &= SPI_INTEN_MASK; -+ reg_val |= bitmap; -+ writel(reg_val, (base_addr + SPI_INT_CTL_REG)); -+} -+ -+/* disable dma irq */ -+void spi_disable_dma_irq(u32 bitmap, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_DMA_CTL_REG); -+ bitmap &= SPI_DRQEN_MASK; -+ reg_val &= ~bitmap; -+ writel(reg_val, base_addr + SPI_DMA_CTL_REG); -+} -+ -+/* enable dma irq */ -+void spi_enable_dma_irq(u32 bitmap, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_DMA_CTL_REG); -+ bitmap &= SPI_DRQEN_MASK; -+ reg_val |= bitmap; -+ writel(reg_val, base_addr + SPI_DMA_CTL_REG); -+} -+ -+/* query irq pending */ -+u32 spi_qry_irq_pending(void *base_addr) -+{ -+ return (SPI_STAT_MASK & readl(base_addr + SPI_STATUS_REG)); -+} -+ -+/* clear irq pending */ -+void spi_clr_irq_pending(u32 pending_bit, void *base_addr) -+{ -+ pending_bit &= SPI_STAT_MASK; -+ writel(pending_bit, base_addr + SPI_STATUS_REG); -+} -+ -+/* query txfifo bytes */ -+u32 spi_query_txfifo(void *base_addr) -+{ -+ u32 reg_val = (SPI_FIFO_TXCNT & readl(base_addr + SPI_FIFO_STA_REG)); -+ reg_val >>= SPI_TXCNT_BIT_POS; -+ return reg_val; -+} -+ -+/* query rxfifo bytes */ -+u32 spi_query_rxfifo(void *base_addr) -+{ -+ u32 reg_val = (SPI_FIFO_RXCNT & readl(base_addr + SPI_FIFO_STA_REG)); -+ reg_val >>= SPI_RXCNT_BIT_POS; -+ return reg_val; -+} -+ -+/* reset fifo */ -+void spi_reset_fifo(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val |= (SPI_CTL_RST_RXFIFO | SPI_CTL_RST_TXFIFO); -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* set transfer total length BC and transfer length WTC */ -+void spi_set_bc_wtc(u32 tx_len, u32 rx_len, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_BC_REG); -+ reg_val &= ~SPI_BC_BC_MASK; -+ reg_val |= (SPI_BC_BC_MASK & (tx_len + rx_len)); -+ writel(reg_val, base_addr + SPI_BC_REG); -+ spi_dbg("%s: bc: %u\n", __func__, readl(base_addr + SPI_BC_REG)); -+ -+ reg_val = readl(base_addr + SPI_TC_REG); -+ reg_val &= ~SPI_TC_WTC_MASK; -+ reg_val |= (SPI_TC_WTC_MASK & tx_len); -+ writel(reg_val, base_addr + SPI_TC_REG); -+ spi_dbg("%s: tc: %u\n", __func__, readl(base_addr + SPI_TC_REG)); -+} -+ -+/* set ss control */ -+void spi_ss_ctrl(void *base_addr, u32 on_off) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ -+ on_off &= 0x1; -+ if (on_off) { -+ reg_val |= SPI_CTL_SS_CTRL; -+ } else { -+ reg_val &= ~SPI_CTL_SS_CTRL; -+ } -+ -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* set ss level */ -+void spi_ss_level(void *base_addr, u32 hi_lo) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ -+ hi_lo &= 0x1; -+ if (hi_lo) { -+ reg_val |= SPI_CTL_SS_LEVEL; -+ } else { -+ reg_val &= ~SPI_CTL_SS_LEVEL; -+ } -+ -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* set wait clock counter */ -+void spi_set_waitclk_cnt(u32 waitclk_cnt, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_WAIT_REG); -+ reg_val &= ~SPI_WAIT_CLK_MASK; -+ waitclk_cnt &= SPI_WAIT_CLK_MASK; -+ reg_val |= waitclk_cnt; -+ writel(reg_val, base_addr + SPI_WAIT_REG); -+} -+ -+/* set sample delay in high speed mode */ -+void spi_set_sample_delay(u32 on_off, void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val &= ~SPI_CTL_MASTER_SDC; -+ reg_val |= on_off; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+/* keep unused burst */ -+void spi_clear_dhb(void *base_addr) -+{ -+ u32 reg_val = readl(base_addr + SPI_CTL_REG); -+ reg_val &= ~SPI_CTL_DHB; -+ writel(reg_val, base_addr + SPI_CTL_REG); -+} -+ -+static int sun7i_spi_get_cfg_csbitmap(int bus_num); -+ -+/* flush d-cache */ -+static void sun7i_spi_cleanflush_dcache_region(void *addr, __u32 len) -+{ -+ __cpuc_flush_dcache_area(addr, len/*len + (1 << 5) * 2 - 2*/); -+} -+ -+static char *spi_dma_rx[] = {"spi0_rx", "spi1_rx", "spi2_rx", "spi3_rx"}; -+static char *spi_dma_tx[] = {"spi0_tx", "spi1_tx", "spi2_tx", "spi3_tx"}; -+ -+static void sun7i_spi_dma_cb_rx(dma_hdl_t dma_hdl, void *parg) -+{ -+ struct sun7i_spi *aw_spi = (struct sun7i_spi *)parg; -+ unsigned long flags; -+ -+ spi_dbg("%s: spi%d dma read data callback\n", __func__, aw_spi->master->bus_num); -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ spi_disable_dma_irq(SPI_DRQEN_RR, aw_spi->base_addr); -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+} -+ -+static void sun7i_spi_dma_cb_tx(dma_hdl_t dma_hdl, void *parg) -+{ -+ struct sun7i_spi *aw_spi = (struct sun7i_spi *)parg; -+ unsigned long flags; -+ -+ spi_dbg("%s: spi%d dma write data callback\n", __func__, aw_spi->master->bus_num); -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ spi_disable_dma_irq(SPI_DRQEN_TE, aw_spi->base_addr); -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+} -+ -+/* request dma channel and set callback function */ -+static int sun7i_spi_prepare_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir) -+{ -+ int ret = 0; -+ int bus_num = aw_spi->master->bus_num; -+ dma_cb_t done_cb; -+ -+ spi_dbg("%s: enter\n", __func__); -+ if (SPI_DMA_RDEV == dma_dir) { -+ aw_spi->dma_hdle_rx = sw_dma_request(spi_dma_rx[bus_num], aw_spi->dma_type); -+ if (!aw_spi->dma_hdle_rx) { -+ spi_err("%s: spi%d request dma rx failed\n", __func__, bus_num); -+ return -EINVAL; -+ } -+ -+ done_cb.func = sun7i_spi_dma_cb_rx; -+ done_cb.parg = aw_spi; -+ ret = sw_dma_ctl(aw_spi->dma_hdle_rx, DMA_OP_SET_FD_CB, (void *)&done_cb); -+ } else if (SPI_DMA_WDEV == dma_dir) { -+ aw_spi->dma_hdle_tx = sw_dma_request(spi_dma_tx[bus_num], aw_spi->dma_type); -+ if (!aw_spi->dma_hdle_tx) { -+ spi_err("%s: spi%d request dma tx failed\n", __func__, bus_num); -+ return -EINVAL; -+ } -+ -+ done_cb.func = sun7i_spi_dma_cb_tx; -+ done_cb.parg = aw_spi; -+ ret = sw_dma_ctl(aw_spi->dma_hdle_tx, DMA_OP_SET_FD_CB, (void *)&done_cb); -+ } else { -+ return -1; -+ } -+ -+ return ret; -+} -+ -+static int sun7i_spi_config_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir, void *buf, unsigned int len) -+{ -+ int bus_num = aw_spi->master->bus_num; -+ unsigned char spi_rx_ndrq[] = {N_SRC_SPI0_RX, N_SRC_SPI1_RX, N_SRC_SPI2_RX, N_SRC_SPI3_RX}; -+ unsigned char spi_tx_ndrq[] = {N_DST_SPI0_TX, N_DST_SPI1_TX, N_DST_SPI3_TX, N_DST_SPI3_TX}; -+ unsigned char spi_rx_ddrq[] = {D_SRC_SPI0_RX, D_SRC_SPI1_RX, D_SRC_SPI2_RX, D_SRC_SPI3_RX}; -+ unsigned char spi_tx_ddrq[] = {D_DST_SPI0_TX, D_DST_SPI1_TX, D_DST_SPI3_TX, D_DST_SPI3_TX}; -+ unsigned long spi_phyaddr[] = {SPI0_BASE_ADDR, SPI1_BASE_ADDR, SPI2_BASE_ADDR, SPI3_BASE_ADDR}; /* physical address */ -+ dma_config_t spi_hw_conf; -+ dma_hdl_t dma_hdle; -+ u32 src_addr, dst_addr; -+ u32 security = SRC_SECU_DST_SECU; -+ -+ spi_dbg("%s: enter\n", __func__); -+ spi_hw_conf.xfer_type.src_data_width = DATA_WIDTH_8BIT; -+ spi_hw_conf.xfer_type.src_bst_len = DATA_BRST_1; -+ spi_hw_conf.xfer_type.dst_data_width = DATA_WIDTH_8BIT; -+ spi_hw_conf.xfer_type.dst_bst_len = DATA_BRST_1; -+ spi_hw_conf.bconti_mode = false; -+ spi_hw_conf.irq_spt = CHAN_IRQ_FD; -+ switch (aw_spi->dma_type) { -+ case CHAN_NORMAL: -+ if (SPI_DMA_RDEV == dma_dir) { -+ spi_hw_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE; -+ spi_hw_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT; -+ spi_hw_conf.src_drq_type = spi_rx_ndrq[bus_num]; -+ spi_hw_conf.dst_drq_type = N_DST_SDRAM; -+ } else if (SPI_DMA_WDEV == dma_dir) { -+ spi_hw_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT; -+ spi_hw_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE; -+ spi_hw_conf.src_drq_type = N_SRC_SDRAM; -+ spi_hw_conf.dst_drq_type = spi_tx_ndrq[bus_num]; -+ } else { -+ return -1; -+ } -+ break; -+ -+ case CHAN_DEDICATE: -+ if (SPI_DMA_RDEV == dma_dir) { -+ spi_hw_conf.address_type.src_addr_mode = DDMA_ADDR_IO; -+ spi_hw_conf.address_type.dst_addr_mode = DDMA_ADDR_LINEAR; -+ spi_hw_conf.src_drq_type = spi_rx_ddrq[bus_num]; -+ spi_hw_conf.dst_drq_type = D_DST_SDRAM; -+ } else if (SPI_DMA_WDEV == dma_dir) { -+ spi_hw_conf.address_type.src_addr_mode = DDMA_ADDR_LINEAR; -+ spi_hw_conf.address_type.dst_addr_mode = DDMA_ADDR_IO; -+ spi_hw_conf.src_drq_type = D_SRC_SDRAM; -+ spi_hw_conf.dst_drq_type = spi_tx_ddrq[bus_num]; -+ } else { -+ return -1; -+ } -+ break; -+ } -+ -+ if (SPI_DMA_RDEV == dma_dir) { -+ dma_hdle = aw_spi->dma_hdle_rx; -+ src_addr = spi_phyaddr[bus_num] + SPI_RXDATA_REG; -+ dst_addr = virt_to_phys(buf); -+ } else if (SPI_DMA_WDEV == dma_dir) { -+ dma_hdle = aw_spi->dma_hdle_tx; -+ src_addr = virt_to_phys(buf); -+ dst_addr = spi_phyaddr[bus_num] + SPI_TXDATA_REG; -+ } else { -+ return -1; -+ } -+ -+ /* set src, dst, drq type, configuration */ -+ if (sw_dma_config(dma_hdle, &spi_hw_conf)) { -+ spi_err("%s: spi%d config failed\n", __func__, bus_num); -+ return -1; -+ } -+ -+ if (sw_dma_ctl(dma_hdle, DMA_OP_SET_SECURITY, &security)) { -+ spi_err("%s: spi%d set dma SRC_SECU_DST_SECU failed\n", __func__, -+ bus_num); -+ return -1; -+ } -+ -+ if (CHAN_DEDICATE == aw_spi->dma_type) { -+ dma_para_t para = { -+ .src_wait_cyc = 0x07, -+ .src_blk_sz = 0x1f, -+ .dst_wait_cyc = 0x07, -+ .dst_blk_sz = 0x1f -+ }; -+ -+ if (sw_dma_ctl(dma_hdle, DMA_OP_SET_PARA_REG, ¶)) { -+ spi_err("%s: spi%d set dma para failed\n", __func__, bus_num); -+ return -1; -+ } -+ } -+ -+ /* 1. flush d-cache */ -+ sun7i_spi_cleanflush_dcache_region((void *)buf, len); -+ -+ /* 2. enqueue dma transfer */ -+ return sw_dma_enqueue(dma_hdle, src_addr, dst_addr, len); -+} -+ -+/* set dma start flag, if queue, it will auto restart to transfer next queue */ -+static int sun7i_spi_start_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir) -+{ -+ int ret = 0; -+ -+ spi_dbg("%s: enter\n", __func__); -+ if (SPI_DMA_RDEV == dma_dir) { -+ ret = sw_dma_ctl(aw_spi->dma_hdle_rx, DMA_OP_START, NULL); -+ } else if (SPI_DMA_WDEV == dma_dir) { -+ ret = sw_dma_ctl(aw_spi->dma_hdle_tx, DMA_OP_START, NULL); -+ } else { -+ return -1; -+ } -+ -+ return ret; -+} -+ -+/* release dma channel, and set queue status to idle. */ -+static int sun7i_spi_release_dma(struct sun7i_spi *aw_spi, enum spi_dma_dir dma_dir) -+{ -+ int ret = 0; -+ unsigned long flags; -+ -+ spi_dbg("%s: enter\n", __func__); -+ if (SPI_DMA_RDEV == dma_dir) { -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ ret = sw_dma_ctl(aw_spi->dma_hdle_rx, DMA_OP_STOP, NULL); -+ ret += sw_dma_release(aw_spi->dma_hdle_rx); -+ aw_spi->dma_hdle_rx = NULL; -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ } else if (SPI_DMA_WDEV == dma_dir) { -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ ret = sw_dma_ctl(aw_spi->dma_hdle_tx, DMA_OP_STOP, NULL); -+ ret += sw_dma_release(aw_spi->dma_hdle_tx); -+ aw_spi->dma_hdle_tx = NULL; -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ } else { -+ return -1; -+ } -+ -+ return ret; -+} -+ -+/* check the valid of cs id */ -+static int sun7i_spi_check_cs(int cs_id, struct sun7i_spi *aw_spi) -+{ -+ int ret = -1; -+ switch (cs_id) { -+ case 0: -+ ret = (aw_spi->cs_bitmap & SPI_CHIP_SELECT_CS0) ? 0 : -1; -+ break; -+ case 1: -+ ret = (aw_spi->cs_bitmap & SPI_CHIP_SELECT_CS1) ? 0 : -1; -+ break; -+ default: -+ spi_err("%s: spi%d chip select not support, cs = %d\n", -+ __func__, aw_spi->master->bus_num, cs_id); -+ break; -+ } -+ -+ return ret; -+} -+ -+/* spi device on or off control */ -+static void sun7i_spi_cs_control(struct spi_device *spi, bool on) -+{ -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master); -+ unsigned int cs = 0; -+ if (aw_spi->cs_control) { -+ if (on) { -+ /* set active */ -+ cs = (spi->mode & SPI_CS_HIGH) ? 1 : 0; -+ } else { -+ /* set inactive */ -+ cs = (spi->mode & SPI_CS_HIGH) ? 0 : 1; -+ } -+ spi_ss_level(aw_spi->base_addr, cs); -+ } -+} -+ -+/* -+ * change the properties of spi device with spi transfer. -+ * every spi transfer must call this interface to update the master to the excute transfer -+ * set clock frequecy, bits per word, mode etc... -+ * return: >= 0 : succeed; < 0: failed. -+ */ -+static int sun7i_spi_xfer_setup(struct spi_device *spi, struct spi_transfer *t) -+{ -+ /* get at the setup function, the properties of spi device */ -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master); -+ struct sun7i_spi_config *config = spi->controller_data; // allocate in the setup, and free in the cleanup -+ void *__iomem base_addr = aw_spi->base_addr; -+ -+ spi_dbg("%s: enter\n", __func__); -+ config->max_speed_hz = (t && t->speed_hz) ? t->speed_hz : spi->max_speed_hz; -+ config->bits_per_word = (t && t->bits_per_word) ? t->bits_per_word : spi->bits_per_word; -+ config->bits_per_word = ((config->bits_per_word + 7) / 8) * 8; -+ -+ if (config->bits_per_word != 8) { -+ spi_err("%s: spi%d just support 8bits per word\n", __func__, spi->master->bus_num); -+ return -EINVAL; -+ } -+ -+ if (spi->chip_select >= spi->master->num_chipselect) { -+ spi_err("%s: spi%d device's chip select = %d exceeds the master supported cs_num[%d] \n", -+ __func__, spi->master->bus_num, spi->chip_select, spi->master->num_chipselect); -+ return -EINVAL; -+ } -+ -+ /* check again board info */ -+ if (sun7i_spi_check_cs(spi->chip_select, aw_spi)) { -+ spi_err("%s: sun7i_spi_check_cs failed, spi_device cs =%d\n", __func__, spi->chip_select); -+ return -EINVAL; -+ } -+ -+ /* set cs */ -+ spi_set_cs(spi->chip_select, base_addr); -+ /* -+ * master: set spi module clock; -+ * set the default frequency 10MHz -+ */ -+ spi_set_master(base_addr); -+ if (config->max_speed_hz > SPI_MAX_FREQUENCY) { -+ return -EINVAL; -+ } -+ -+#ifndef CONFIG_AW_FPGA_PLATFORM -+ spi_set_clk(config->max_speed_hz, clk_get_rate(aw_spi->mclk), base_addr); -+#else -+ spi_set_clk(config->max_speed_hz, 24000000, base_addr); -+#endif -+ -+ /* -+ * master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0. -+ * set bit width-default: 8 bits -+ */ -+ spi_config(1, spi->mode, base_addr); -+ -+ return 0; -+} -+ -+/* -+ * <= 64 : cpu ; > 64 : dma -+ * wait for done completion in this function, wakup in the irq hanlder -+ */ -+static int sun7i_spi_xfer(struct spi_device *spi, struct spi_transfer *t) -+{ -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master); -+ void __iomem *base_addr = aw_spi->base_addr; -+ unsigned long flags = 0; -+ unsigned tx_len = t->len; -+ unsigned rx_len = t->len; -+ unsigned char *rx_buf = (unsigned char *)t->rx_buf; -+ unsigned char *tx_buf = (unsigned char *)t->tx_buf; -+ enum spi_dma_dir dma_dir = SPI_DMA_NULL; -+ int ret = 0; -+ -+ spi_dbg("%s: spi%d begin transfer, txbuf %p, rxbuf %p, len %d\n", -+ __func__, spi->master->bus_num, tx_buf, rx_buf, t->len); -+ -+ if ((!t->tx_buf && !t->rx_buf) || !t->len) { -+ spi_err("%s: spi%d invalid buffer or len\n", __func__, spi->master->bus_num); -+ return -EINVAL; -+ } -+ -+ /* write 1 to clear 0 */ -+ spi_clr_irq_pending(SPI_STAT_MASK, base_addr); -+ /* disable all DRQ */ -+ spi_disable_dma_irq(SPI_DRQEN_MASK, base_addr); -+ /* reset tx/rx fifo */ -+ spi_reset_fifo(base_addr); -+ -+ if (aw_spi->duplex_flag != DUPLEX_NULL) { -+ spi_err("%s: spi%d duplex flag not null\n", __func__, spi->master->bus_num); -+ return -EINVAL; -+ } -+ -+ /* set the Burst Counter and Write Transmit Counter, auto put dummy data into the txFIFO! */ -+ /* check if use full duplex or half duplex */ -+ if (tx_buf && rx_buf) { -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ aw_spi->duplex_flag = FULL_DUPLEX_RX_TX; -+ spi_set_bc_wtc(tx_len, 0, base_addr); -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ } else { -+ if (tx_buf) { -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ aw_spi->duplex_flag = HALF_DUPLEX_TX; -+ spi_set_bc_wtc(tx_len, 0, base_addr); -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ } else if (rx_buf) { -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ aw_spi->duplex_flag = HALF_DUPLEX_RX; -+ spi_set_bc_wtc(0, rx_len, base_addr); -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ } -+ } -+ -+ /* -+ * 1. Tx/Rx error irq, process in IRQ; -+ * 2. Transfer Complete Interrupt Enable -+ */ -+ spi_enable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr); -+ -+ /* > 64 use DMA transfer, or use cpu */ -+ if (t->len > BULK_DATA_BOUNDARY) { -+ spi_sel_dma_type(aw_spi->dma_type, base_addr); -+ -+ switch (aw_spi->duplex_flag) { -+ case HALF_DUPLEX_RX: { -+ spi_enable_dma_irq(SPI_DRQEN_RR, base_addr); -+ -+ dma_dir |= SPI_DMA_RDEV; -+ ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_RDEV); -+ if (ret < 0) { -+ spi_disable_dma_irq(SPI_DRQEN_RR, base_addr); -+ spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr); -+ return -EINVAL; -+ } -+ -+ sun7i_spi_config_dma(aw_spi, SPI_DMA_RDEV, (void *)rx_buf, rx_len); -+ sun7i_spi_start_dma(aw_spi, SPI_DMA_RDEV); -+ -+ spi_start_xfer(base_addr); -+ break; -+ } -+ -+ case HALF_DUPLEX_TX: { -+ spi_start_xfer(base_addr); -+ spi_enable_dma_irq(SPI_DRQEN_TE, base_addr); -+ -+ dma_dir |= SPI_DMA_WDEV; -+ ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_WDEV); -+ if (ret < 0) { -+ spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr); -+ spi_disable_dma_irq(SPI_DRQEN_TE, base_addr); -+ return -EINVAL; -+ } -+ -+ sun7i_spi_config_dma(aw_spi, SPI_DMA_WDEV, (void *)tx_buf, tx_len); -+ sun7i_spi_start_dma(aw_spi, SPI_DMA_WDEV); -+ break; -+ } -+ -+ case FULL_DUPLEX_RX_TX: { -+ spi_enable_dma_irq(SPI_DRQEN_RR, base_addr); -+ -+ dma_dir |= SPI_DMA_RDEV; -+ ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_RDEV); -+ if (ret < 0) { -+ spi_disable_dma_irq(SPI_DRQEN_RR, base_addr); -+ spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr); -+ return -EINVAL; -+ } -+ -+ sun7i_spi_config_dma(aw_spi, SPI_DMA_RDEV, (void *)rx_buf, rx_len); -+ sun7i_spi_start_dma(aw_spi, SPI_DMA_RDEV); -+ -+ spi_start_xfer(base_addr); -+ spi_enable_dma_irq(SPI_DRQEN_TE, base_addr); -+ -+ dma_dir |= SPI_DMA_WDEV; -+ ret = sun7i_spi_prepare_dma(aw_spi, SPI_DMA_WDEV); -+ if (ret < 0) { -+ spi_disable_irq(SPI_INTEN_TC | SPI_INTEN_ERR, base_addr); -+ spi_disable_dma_irq(SPI_DRQEN_TE, base_addr); -+ return -EINVAL; -+ } -+ -+ sun7i_spi_config_dma(aw_spi, SPI_DMA_WDEV, (void *)tx_buf, tx_len); -+ sun7i_spi_start_dma(aw_spi, SPI_DMA_WDEV); -+ break; -+ } -+ -+ default: -+ return -1; -+ } -+ } else { -+ switch (aw_spi->duplex_flag) { -+ case HALF_DUPLEX_RX: { -+ unsigned int poll_time = 0x7ffff; -+ /* SMC=1,XCH trigger the transfer */ -+ spi_start_xfer(base_addr); -+ while (rx_len && (--poll_time > 0)) { -+ /* rxFIFO counter */ -+ if (spi_query_rxfifo(base_addr)) { -+ *rx_buf++ = readb(base_addr + SPI_RXDATA_REG); -+ --rx_len; -+ } -+ } -+ if (poll_time <= 0) { -+ spi_err("%s: spi%d cpu receive data time out\n", -+ __func__, spi->master->bus_num); -+ } -+ break; -+ } -+ case HALF_DUPLEX_TX: { -+ unsigned int poll_time = 0xfffff; -+ spi_start_xfer(base_addr); -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ for (; tx_len > 0; --tx_len) { -+ writeb(*tx_buf++, base_addr + SPI_TXDATA_REG); -+ } -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ while (spi_query_txfifo(base_addr) && (--poll_time > 0)); /* txFIFO counter */ -+ if (poll_time <= 0) { -+ spi_err("%s: spi%d cpu transfer data time out\n", -+ __func__, spi->master->bus_num); -+ } -+ break; -+ } -+ case FULL_DUPLEX_RX_TX: { -+ unsigned int poll_time_tx = 0xfffff; -+ unsigned int poll_time_rx = 0x7ffff; -+ -+ if ((rx_len == 0) || (tx_len == 0)) -+ return -EINVAL; -+ -+ spi_start_xfer(base_addr); -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ for (; tx_len > 0; --tx_len) { -+ writeb(*tx_buf++, base_addr + SPI_TXDATA_REG); -+ } -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ while (spi_query_txfifo(base_addr) && (--poll_time_tx > 0)); /* txFIFO counter */ -+ if (poll_time_tx <= 0) { -+ spi_err("%s: spi%d cpu transfer data time out\n", -+ __func__, spi->master->bus_num); -+ break; -+ } -+ -+ while (rx_len && (--poll_time_rx > 0)) { -+ /* rxFIFO counter */ -+ if (spi_query_rxfifo(base_addr)) { -+ *rx_buf++ = readb(base_addr + SPI_RXDATA_REG); -+ --rx_len; -+ } -+ } -+ if (poll_time_rx <= 0) { -+ spi_err("%s: spi%d cpu receive data time out\n", -+ __func__, spi->master->bus_num); -+ } -+ break; -+ } -+ default: -+ return -1; -+ } -+ } -+ -+ /* wait for xfer complete in the isr. */ -+ spi_dbg("%s: spi%d wait for xfer complete\n", __func__, spi->master->bus_num); -+ wait_for_completion(&aw_spi->done); -+ /* get the isr return code */ -+ if (aw_spi->result != 0) { -+ spi_err("%s: spi%d xfer failed\n", __func__, spi->master->bus_num); -+ ret = -1; -+ } -+ -+ /* release dma resource if neccessary */ -+ if (SPI_DMA_RDEV & dma_dir) { -+ sun7i_spi_release_dma(aw_spi, SPI_DMA_RDEV); -+ } -+ if (SPI_DMA_WDEV & dma_dir) { -+ sun7i_spi_release_dma(aw_spi, SPI_DMA_WDEV); -+ } -+ -+ if (aw_spi->duplex_flag != DUPLEX_NULL) { -+ aw_spi->duplex_flag = DUPLEX_NULL; -+ } -+ -+ return ret; -+} -+ -+/* spi core xfer process */ -+static void sun7i_spi_work(struct work_struct *work) -+{ -+ struct sun7i_spi *aw_spi = container_of(work, struct sun7i_spi, work); -+ spin_lock_irq(&aw_spi->lock); -+ aw_spi->busy = SPI_BUSY; -+ -+ spi_dbg("%s: enter\n", __func__); -+ /* -+ * get from messages queue, and then do with them, -+ * if message queue is empty ,then return and set status to free, -+ * otherwise process them. -+ */ -+ while (!list_empty(&aw_spi->queue)) { -+ struct spi_message *msg = NULL; -+ struct spi_device *spi = NULL; -+ struct spi_transfer *t = NULL; -+ unsigned int cs_change = 0; -+ int status; -+ /* get message from message queue in sun7i_spi. */ -+ msg = container_of(aw_spi->queue.next, struct spi_message, queue); -+ /* then delete from the message queue,now it is alone.*/ -+ list_del_init(&msg->queue); -+ spin_unlock_irq(&aw_spi->lock); -+ /* get spi device from this message */ -+ spi = msg->spi; -+ /* set default value,no need to change cs,keep select until spi transfer require to change cs. */ -+ cs_change = 1; -+ /* set message status to succeed. */ -+ status = -1; -+ /* search the spi transfer in this message, deal with it alone. */ -+ list_for_each_entry(t, &msg->transfers, transfer_list) { -+ if ((status == -1) || t->bits_per_word || t->speed_hz) { /* if spi transfer is zero,use spi device value. */ -+ status = sun7i_spi_xfer_setup(spi, t); /* set the value every spi transfer */ -+ if (status < 0) -+ break; /* fail, quit */ -+ spi_dbg("[spi-%d]: xfer setup \n", aw_spi->master->bus_num); -+ } -+ -+ /* first active the cs */ -+ if (cs_change) -+ aw_spi->cs_control(spi, 1); -+ /* update the new cs value */ -+ cs_change = t->cs_change; -+ -+ /* full duplex mode */ -+ if (t->rx_buf && t->tx_buf) -+ spi_clear_dhb(aw_spi->base_addr); -+ -+ /* -+ * do transfer -+ * > 64 : dma ; <= 64 : cpu -+ * wait for done completion in this function, wakup in the irq hanlder -+ */ -+ status = sun7i_spi_xfer(spi, t); -+ if (status) -+ break; /* fail quit, zero means succeed */ -+ -+ /* accmulate the value in the message */ -+ msg->actual_length += t->len; -+ /* may be need to delay */ -+ if (t->delay_usecs) -+ udelay(t->delay_usecs); -+ -+ /* if zero, keep active, otherwise deactived. */ -+ if (cs_change) -+ aw_spi->cs_control(spi, 0); -+ } -+ -+ /* -+ * spi message complete,succeed or failed -+ * return value -+ */ -+ msg->status = status; -+ /* wakup the uplayer caller,complete one message */ -+ msg->complete(msg->context); -+ /* fail or need to change cs */ -+ if (status || !cs_change) { -+ aw_spi->cs_control(spi, 0); -+ } -+ -+ /* restore default value. */ -+ //sun7i_spi_xfer_setup(spi, NULL); -+ spin_lock_irq(&aw_spi->lock); -+ } -+ -+ /* set spi to free */ -+ aw_spi->busy = SPI_FREE; -+ spin_unlock_irq(&aw_spi->lock); -+} -+ -+/* wake up the sleep thread, and give the result code */ -+static irqreturn_t sun7i_spi_handler(int irq, void *dev_id) -+{ -+ struct sun7i_spi *aw_spi = (struct sun7i_spi *)dev_id; -+ void *base_addr = aw_spi->base_addr; -+ -+ unsigned int status = spi_qry_irq_pending(base_addr); -+ spi_clr_irq_pending(status, base_addr); -+ -+ aw_spi->result = 0; /* assume succeed */ -+ /* master mode, Transfer Complete Interrupt */ -+ if (status & SPI_STAT_TC) { -+ spi_dbg("%s: spi%d TC comes, irq status = 0x%08x\n", -+ __func__, aw_spi->master->bus_num, status); -+ spi_disable_irq(SPI_STAT_TC | SPI_STAT_ERR, base_addr); -+ /* -+ * just check dma+callback receive, skip other condition. -+ * dma+callback receive: when TC comes, dma may be still not complete fetch data from rxFIFO. -+ * other receive: cpu or dma+poll, just skip this. -+ */ -+ if (aw_spi->dma_hdle_rx) { -+ unsigned int poll_time = 0xffff; -+ /* during poll, dma maybe complete rx, rx_dma_used is 0. then return.*/ -+ while (spi_query_rxfifo(base_addr) && (--poll_time > 0)); -+ if (poll_time <= 0) { -+ spi_err("%s: spi%d dma callback method, rx data time out in irq\n", -+ __func__, aw_spi->master->bus_num); -+ aw_spi->result = -1; -+ complete(&aw_spi->done); -+ return -1; -+ } else if (poll_time < 0xffff) { -+ spi_dbg("%s: spi%d rx irq comes first, dma last. wait = 0x%x\n", -+ __func__, aw_spi->master->bus_num, poll_time); -+ } -+ } -+ -+ /* wakup uplayer, by the sem */ -+ complete(&aw_spi->done); -+ return IRQ_HANDLED; -+ } -+ /* master mode: err */ -+ else if (status & SPI_STAT_ERR) { -+ spi_err("%s: spi%d ERR comes, irq status = 0x%08x\n", -+ __func__, aw_spi->master->bus_num, status); -+ /* error process, release dma in the workqueue,should not be here */ -+ spi_disable_irq(SPI_STAT_TC | SPI_STAT_ERR, base_addr); -+ spi_restore_state(1, base_addr); -+ aw_spi->result = -1; -+ complete(&aw_spi->done); -+ spi_err("%s: spi%d master mode error: txFIFO overflow/rxFIFO underrun or overflow\n", -+ __func__, aw_spi->master->bus_num); -+ return IRQ_HANDLED; -+ } -+ -+ spi_dbg("%s: spi%d NONE comes\n", __func__, aw_spi->master->bus_num); -+ return IRQ_NONE; -+} -+ -+/* interface 1 */ -+static int sun7i_spi_transfer(struct spi_device *spi, struct spi_message *msg) -+{ -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master); -+ unsigned long flags; -+ msg->actual_length = 0; -+ msg->status = -EINPROGRESS; -+ -+ spi_dbg("%s: enter\n", __func__); -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ /* add msg to the sun7i_spi queue */ -+ list_add_tail(&msg->queue, &aw_spi->queue); -+ /* add work to the workqueue,schedule the cpu. */ -+ queue_work(aw_spi->workqueue, &aw_spi->work); -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ /* return immediately and wait for completion in the uplayer caller. */ -+ return 0; -+} -+ -+/* interface 2, setup the frequency and default status */ -+static int sun7i_spi_setup(struct spi_device *spi) -+{ -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(spi->master); -+ struct sun7i_spi_config *config = spi->controller_data; /* general is null. */ -+ unsigned long flags; -+ -+ spi_dbg("%s: enter\n", __func__); -+ /* just support 8 bits per word */ -+ if (spi->bits_per_word != 8) -+ return -EINVAL; -+ /* first check its valid,then set it as default select,finally set its */ -+ if (sun7i_spi_check_cs(spi->chip_select, aw_spi)) { -+ spi_err("%s: spi%d not support cs-%d \n", __func__, -+ spi->master->bus_num, spi->chip_select); -+ return -EINVAL; -+ } -+ -+ if (spi->max_speed_hz > SPI_MAX_FREQUENCY) -+ return -EINVAL; -+ -+ if (!config) { -+ config = kzalloc(sizeof * config, GFP_KERNEL); -+ if (!config) { -+ spi_err("%s: spi%d alloc memory for config failed\n", -+ __func__, spi->master->bus_num); -+ return -ENOMEM; -+ } -+ spi->controller_data = config; -+ } -+ -+ /* -+ * set the default vaule with spi device -+ * can change by every spi transfer -+ */ -+ config->bits_per_word = spi->bits_per_word; -+ config->max_speed_hz = spi->max_speed_hz; -+ config->mode = spi->mode; -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ /* if aw16xx spi is free, then deactived the spi device */ -+ if (aw_spi->busy & SPI_FREE) { -+ /* set chip select number */ -+ spi_set_cs(spi->chip_select, aw_spi->base_addr); -+ /* deactivate chip select */ -+ aw_spi->cs_control(spi, 0); -+ } -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ return 0; -+} -+ -+/* interface 3 */ -+static void sun7i_spi_cleanup(struct spi_device *spi) -+{ -+ if (spi->controller_data) { -+ kfree(spi->controller_data); -+ spi->controller_data = NULL; -+ } -+} -+ -+static int sun7i_spi_set_gpio(struct sun7i_spi *aw_spi, bool on) -+{ -+ if(on) { -+ if(aw_spi->master->bus_num == 0) { -+ aw_spi->gpio_hdle = gpio_request_ex("spi0_para", NULL); -+ if(!aw_spi->gpio_hdle) { -+ spi_err("spi0 request gpio fail!\n"); -+ return -1; -+ } -+ //hex_dump("gpio regs:", (void __iomem*)SW_VA_PORTC_IO_BASE, 0x200, 2); -+ } -+ else if(aw_spi->master->bus_num == 1) { -+ /** -+ * PI8 SPI1_CS0 -+ * PI9 SPI1_CS1 -+ * PI10 SPI1_CLK -+ * PI11 SPI1_MOSI -+ * PI12 SPI1_MISO -+ */ -+ #ifndef SYS_SPI_PIN -+ unsigned int reg_val = readl(_Pn_CFG1(8)); -+ /* set spi function */ -+ reg_val &= ~0x77777; -+ reg_val |= 0x22222; -+ writel(reg_val, _Pn_CFG1(8)); -+ /* set pull up */ -+ reg_val = readl(_Pn_PUL1(8)); -+ reg_val &= ~(0x3ff<<16); -+ reg_val |= (0x155<<16); -+ writel(reg_val, _Pn_PUL1(8)); -+ /* no need to set driver,default is driver 1. */ -+ #else -+ aw_spi->gpio_hdle = gpio_request_ex("spi1_para", NULL); -+ if(!aw_spi->gpio_hdle) { -+ spi_err("spi1 request gpio fail!\n"); -+ return -1; -+ } -+ #endif -+ } -+ else if(aw_spi->master->bus_num == 2) { -+ aw_spi->gpio_hdle = gpio_request_ex("spi2_para", NULL); -+ if(!aw_spi->gpio_hdle) { -+ spi_err("spi2 request gpio fail!\n"); -+ return -1; -+ } -+ } -+ -+ #ifdef AW1623_FPGA -+ { -+ #include -+ void __iomem* pi_cfg0 = (void __iomem*)(SW_VA_PORTC_IO_BASE+0x48); -+ u32 rval = readl(pi_cfg0) & (~(0x70777)); -+ writel(rval|(0x30333), pi_cfg0); -+ } -+ #endif -+ } -+ else { -+ if(aw_spi->master->bus_num == 0) { -+ gpio_release(aw_spi->gpio_hdle, 0); -+ } -+ else if(aw_spi->master->bus_num == 1) { -+ #ifndef SYS_SPI_PIN -+ unsigned int reg_val = readl(_Pn_CFG1(8)); -+ /* set default */ -+ reg_val &= ~0x77777; -+ writel(reg_val, _Pn_CFG1(8)); -+ #else -+ gpio_release(aw_spi->gpio_hdle, 0); -+ #endif -+ } -+ else if(aw_spi->master->bus_num == 2) { -+ gpio_release(aw_spi->gpio_hdle, 0); -+ } -+ } -+ return 0; -+} -+ -+static int sun7i_spi_set_mclk(struct sun7i_spi *aw_spi, u32 mod_clk) -+{ -+ struct clk *source_clock = NULL; -+ char *name = NULL; -+#ifdef CONFIG_AW_FPGA_PLATFORM -+// u32 source = 0; -+#elif defined CONFIG_AW_ASIC_PLATFORM -+// u32 source = 2; -+#endif -+ u32 source = 1; -+ int ret = 0; -+ -+ switch (source) { -+ case 0: -+ source_clock = clk_get(NULL, "hosc"); -+ name = "hosc"; -+ break; -+ case 1: -+ source_clock = clk_get(NULL, "sdram_pll_p"); -+ name = "sdram_pll_p"; -+ break; -+ case 2: -+ source_clock = clk_get(NULL, "sata_pll"); -+ name = "sata_pll"; -+ break; -+ default: -+ return -1; -+ } -+ -+ if (IS_ERR(source_clock)) { -+ ret = PTR_ERR(source_clock); -+ spi_err("%s: Unable to get spi%d source clock resource\n", -+ __func__, aw_spi->master->bus_num); -+ return -1; -+ } -+ -+ if (clk_set_parent(aw_spi->mclk, source_clock)) { -+ spi_err("%s: spi%d clk_set_parent failed\n", __func__, -+ aw_spi->master->bus_num); -+ ret = -1; -+ goto out; -+ } -+ -+ if (clk_set_rate(aw_spi->mclk, mod_clk)) { -+ spi_err("%s: spi%d clk_set_rate failed\n", __func__, -+ aw_spi->master->bus_num); -+ ret = -1; -+ goto out; -+ } -+ -+ spi_inf("%s: spi%d source = %s, src_clk = %u, mclk %u\n", -+ __func__, aw_spi->master->bus_num, name, -+ (unsigned)clk_get_rate(source_clock), (unsigned)clk_get_rate(aw_spi->mclk)); -+ -+ if (clk_enable(aw_spi->mclk)) { -+ spi_err("%s: spi%d couldn't enable module clock 'spi'\n", -+ __func__, aw_spi->master->bus_num); -+ ret = -EBUSY; -+ goto out; -+ } -+ -+ ret = 0; -+out: -+ clk_put(source_clock); -+ return ret; -+} -+ -+static int sun7i_spi_hw_init(struct sun7i_spi *aw_spi) -+{ -+ void *base_addr = aw_spi->base_addr; -+ unsigned long sclk_freq = 0; -+ char *mclk_name[] = {"spi0", "spi1", "spi2", "spi3"}; -+ -+ aw_spi->mclk = clk_get(&aw_spi->pdev->dev, mclk_name[aw_spi->pdev->id]); -+ if (IS_ERR(aw_spi->mclk)) { -+ spi_err("%s: spi%d unable to acquire module clock 'spi'\n", -+ __func__, aw_spi->master->bus_num); -+ return -1; -+ } -+ if (sun7i_spi_set_mclk(aw_spi, 100000000)) { -+ spi_err("%s: spi%d sun7i_spi_set_mclk 'spi'\n", -+ __func__, aw_spi->master->bus_num); -+ clk_put(aw_spi->mclk); -+ return -1; -+ } -+ -+ /* 1. enable the spi module */ -+ spi_enable_bus(base_addr); -+ -+ /* 2. set the default chip select */ -+ if (sun7i_spi_check_cs(0, aw_spi)) { -+ spi_set_cs(1, base_addr); -+ } else { -+ spi_set_cs(0, base_addr); -+ } -+ -+ /* -+ * 3. master: set spi module clock; -+ * 4. set the default frequency 10MHz -+ */ -+ spi_set_master(base_addr); -+ sclk_freq = clk_get_rate(aw_spi->mclk); -+ spi_set_clk(10000000, sclk_freq, base_addr); -+ -+ /* -+ * 5. master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0. -+ * 6. set bit width-default: 8 bits -+ */ -+ spi_config(1, SPI_MODE_0, base_addr); -+ -+ /* 7. manual control the chip select */ -+ spi_ss_ctrl(base_addr, 1); -+ return 0; -+} -+ -+static int sun7i_spi_hw_exit(struct sun7i_spi *aw_spi) -+{ -+ /* disable the spi controller */ -+ spi_disable_bus(aw_spi->base_addr); -+ /* disable module clock */ -+ clk_disable(aw_spi->mclk); -+ clk_put(aw_spi->mclk); -+ -+ return 0; -+} -+ -+static int __init sun7i_spi_probe(struct platform_device *pdev) -+{ -+ struct resource *mem_res, *dma_res_rx, *dma_res_tx; -+ struct sun7i_spi *aw_spi; -+ struct sun7i_spi_platform_data *pdata; -+ struct spi_master *master; -+ int ret = 0, err = 0, irq; -+ int cs_bitmap = 0; -+ -+ if (pdev->id < 0) { -+ spi_err("%s: invalid platform device id: %d\n", __func__, pdev->id); -+ return -ENODEV; -+ } -+ -+ if (pdev->dev.platform_data == NULL) { -+ spi_err("%s: platform_data missing\n", __func__); -+ return -ENODEV; -+ } -+ -+ pdata = pdev->dev.platform_data; -+ if (!pdata->clk_name) { -+ spi_err("%s: platform data must initialize\n", __func__); -+ return -EINVAL; -+ } -+ -+ /* Check for availability of necessary resource */ -+ dma_res_rx = platform_get_resource(pdev, IORESOURCE_DMA, 0); -+ if (dma_res_rx == NULL) { -+ spi_err("%s: Unable to get spi DMA RX resource\n", __func__); -+ return -ENXIO; -+ } -+ -+ dma_res_tx = platform_get_resource(pdev, IORESOURCE_DMA, 1); -+ if (dma_res_tx == NULL) { -+ spi_err("%s: Unable to get spi DMA TX resource\n", __func__); -+ return -ENXIO; -+ } -+ -+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (mem_res == NULL) { -+ spi_err("%s: Unable to get spi MEM resource\n", __func__); -+ return -ENXIO; -+ } -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ spi_err("%s: NO SPI IRQ specified\n", __func__); -+ return -ENXIO; -+ } -+ -+ /* create spi master */ -+ master = spi_alloc_master(&pdev->dev, sizeof(struct sun7i_spi)); -+ if (master == NULL) { -+ spi_err("%s: Unable to allocate SPI Master\n", __func__); -+ return -ENOMEM; -+ } -+ -+ platform_set_drvdata(pdev, master); -+ aw_spi = spi_master_get_devdata(master); -+ memset(aw_spi, 0, sizeof(struct sun7i_spi)); -+ -+ aw_spi->master = master; -+ aw_spi->irq = irq; -+#ifdef CONFIG_SUN7I_SPI_NDMA -+ aw_spi->dma_type = CHAN_NORMAL; -+#else -+ aw_spi->dma_type = CHAN_DEDICATE; -+#endif -+ spi_inf("%s: spi%d dma type: %s\n", __func__, pdev->id, -+ aw_spi->dma_type == CHAN_NORMAL ? "normal" : "dedicate"); -+ aw_spi->dma_id_tx = dma_res_tx->start; -+ aw_spi->dma_id_rx = dma_res_rx->start; -+ aw_spi->dma_hdle_rx = 0; -+ aw_spi->dma_hdle_tx = 0; -+ aw_spi->cs_control = sun7i_spi_cs_control; -+ aw_spi->cs_bitmap = pdata->cs_bitmap; /* cs0-0x1; cs1-0x2; cs0&cs1-0x3. */ -+ aw_spi->busy = SPI_FREE; -+ aw_spi->duplex_flag = DUPLEX_NULL; -+ -+ master->bus_num = pdev->id; -+ master->setup = sun7i_spi_setup; -+ master->cleanup = sun7i_spi_cleanup; -+ master->transfer = sun7i_spi_transfer; -+ master->num_chipselect = pdata->num_cs; -+ /* the spi->mode bits understood by this driver: */ -+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; -+ -+ /* update the cs bitmap */ -+ cs_bitmap = sun7i_spi_get_cfg_csbitmap(pdev->id); -+ if (cs_bitmap & 0x3) { -+ aw_spi->cs_bitmap = cs_bitmap & 0x3; -+ spi_inf("%s: spi%d cs bitmap: 0x%x\n", -+ __func__, master->bus_num, cs_bitmap); -+ } -+ -+ err = request_irq(aw_spi->irq, sun7i_spi_handler, IRQF_DISABLED, pdev->name, aw_spi); -+ if (err) { -+ spi_err("%s: Cannot claim IRQ\n", __func__); -+ goto err0; -+ } -+ -+ if (request_mem_region(mem_res->start, -+ resource_size(mem_res), pdev->name) == NULL) { -+ spi_err("%s: Req mem region failed\n", __func__); -+ ret = -ENXIO; -+ goto err1; -+ } -+ -+ aw_spi->base_addr = ioremap(mem_res->start, resource_size(mem_res)); -+ if (aw_spi->base_addr == NULL) { -+ spi_err("%s: Unable to remap IO\n", __func__); -+ ret = -ENXIO; -+ goto err2; -+ } -+ -+ /* Setup clocks */ -+ aw_spi->hclk = clk_get(&pdev->dev, pdata->clk_name); -+ if (IS_ERR(aw_spi->hclk)) { -+ spi_err("%s: Unable to get clock 'spi'\n", __func__); -+ ret = PTR_ERR(aw_spi->hclk); -+ goto err3; -+ } -+ -+ if (clk_enable(aw_spi->hclk)) { -+ spi_err("%s: Couldn't enable clock 'spi'\n", __func__); -+ ret = -EBUSY; -+ goto err4; -+ } -+ -+ aw_spi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent)); -+ if (aw_spi->workqueue == NULL) { -+ spi_err("%s: Unable to create workqueue\n", __func__); -+ ret = -ENOMEM; -+ goto err5; -+ } -+ -+ aw_spi->pdev = pdev; -+ -+ /* Setup Deufult Mode */ -+ sun7i_spi_hw_init(aw_spi); -+ -+#ifndef SYS_SPI_PIN -+ /* set gpio */ -+ gpio_addr = ioremap(_PIO_BASE_ADDRESS, 0x1000); -+#endif -+ sun7i_spi_set_gpio(aw_spi, 1); -+ -+ spin_lock_init(&aw_spi->lock); -+ init_completion(&aw_spi->done); -+ INIT_WORK(&aw_spi->work, sun7i_spi_work); /* banding the process handler */ -+ INIT_LIST_HEAD(&aw_spi->queue); -+ -+ if (spi_register_master(master)) { -+ spi_err("%s: Cannot register SPI master\n", __func__); -+ ret = -EBUSY; -+ goto err6; -+ } -+ -+ spi_inf("%s: reuuimlla's SoC SPI Driver loaded for Bus SPI%d with %d Slaves at most\n", -+ __func__, pdev->id, master->num_chipselect); -+ spi_inf("%s: spi%d driver probe succeed, base %p, irq %d, dma_id_rx %d, dma_id_tx %d\n", -+ __func__, master->bus_num, aw_spi->base_addr, aw_spi->irq, -+ aw_spi->dma_id_rx, aw_spi->dma_id_tx); -+ return 0; -+ -+err6: -+ destroy_workqueue(aw_spi->workqueue); -+err5: -+ iounmap((void *)aw_spi->base_addr); -+err4: -+err3: -+err2: -+ release_mem_region(mem_res->start, resource_size(mem_res)); -+err1: -+ free_irq(aw_spi->irq, aw_spi); -+err0: -+ platform_set_drvdata(pdev, NULL); -+ spi_master_put(master); -+ return ret; -+} -+ -+static int sun7i_spi_remove(struct platform_device *pdev) -+{ -+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(master); -+ struct resource *mem_res; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ aw_spi->busy |= SPI_FREE; -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ while (aw_spi->busy & SPI_BUSY) { -+ spi_inf("%s: spi%d busy\n", __func__, master->bus_num); -+ msleep(10); -+ } -+ -+ sun7i_spi_hw_exit(aw_spi); -+ spi_unregister_master(master); -+ destroy_workqueue(aw_spi->workqueue); -+ -+ clk_disable(aw_spi->hclk); -+ clk_put(aw_spi->hclk); -+ -+ iounmap((void *) aw_spi->base_addr); -+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (mem_res != NULL) -+ release_mem_region(mem_res->start, resource_size(mem_res)); -+ -+ platform_set_drvdata(pdev, NULL); -+ spi_master_put(master); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int sun7i_spi_suspend(struct platform_device *pdev, pm_message_t state) -+{ -+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(master); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ aw_spi->busy |= SPI_SUSPND; -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ while (aw_spi->busy & SPI_BUSY) { -+ spi_inf("%s: spi%d busy\n", __func__, master->bus_num); -+ msleep(10); -+ } -+ -+ /* Disable the clock */ -+ clk_disable(aw_spi->hclk); -+ -+ spi_inf("%s: spi%d suspend okay...\n", __func__, master->bus_num); -+ return 0; -+} -+ -+static int sun7i_spi_resume(struct platform_device *pdev) -+{ -+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); -+ struct sun7i_spi *aw_spi = spi_master_get_devdata(master); -+ unsigned long flags; -+ -+ /* Enable the clock */ -+ clk_enable(aw_spi->hclk); -+ sun7i_spi_hw_init(aw_spi); -+ -+ spin_lock_irqsave(&aw_spi->lock, flags); -+ aw_spi->busy = SPI_FREE; -+ spin_unlock_irqrestore(&aw_spi->lock, flags); -+ -+ spi_inf("%s: spi%d resume okay...\n", __func__, master->bus_num); -+ return 0; -+} -+#else -+#define sun7i_spi_suspend NULL -+#define sun7i_spi_resume NULL -+#endif /* CONFIG_PM */ -+ -+static struct platform_driver sun7i_spi_driver = { -+ .probe = sun7i_spi_probe, -+ .remove = sun7i_spi_remove, -+ .suspend = sun7i_spi_suspend, -+ .resume = sun7i_spi_resume, -+ .driver = { -+ .name = "sun7i-spi", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+/* spi resouce and platform data start */ -+struct sun7i_spi_platform_data sun7i_spi0_pdata = { -+ .cs_bitmap = 0x3, -+ .num_cs = 2, -+ .clk_name = "ahb_spi0", -+}; -+ -+static struct resource sun7i_spi0_resources[] = { -+ [0] = { -+ .start = SPI0_BASE_ADDR, -+ .end = SPI0_BASE_ADDR + 1024, -+ .flags = IORESOURCE_MEM, -+ }, -+#ifdef CONFIG_SUN7I_SPI_NDMA -+ [1] = { -+ .start = N_SRC_SPI0_RX, -+ .end = N_SRC_SPI0_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI0_TX, -+ .end = N_DST_SPI0_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#else -+ [1] = { -+ .start = D_SRC_SPI0_RX, -+ .end = D_SRC_SPI0_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = D_DST_SPI0_TX, -+ .end = D_DST_SPI0_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#endif -+ [3] = { -+ .start = SW_INT_IRQNO_SPI00, -+ .end = SW_INT_IRQNO_SPI00, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device sun7i_spi0_device = { -+ .name = "sun7i-spi", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(sun7i_spi0_resources), -+ .resource = sun7i_spi0_resources, -+ .dev = { -+ .platform_data = &sun7i_spi0_pdata, -+ }, -+}; -+ -+struct sun7i_spi_platform_data sun7i_spi1_pdata = { -+ .cs_bitmap = 0x3, -+ .num_cs = 2, -+ .clk_name = "ahb_spi1", -+}; -+ -+static struct resource sun7i_spi1_resources[] = { -+ [0] = { -+ .start = SPI1_BASE_ADDR, -+ .end = SPI1_BASE_ADDR + 1024, -+ .flags = IORESOURCE_MEM, -+ }, -+#ifdef CONFIG_SUN7I_SPI_NDMA -+ [1] = { -+ .start = N_SRC_SPI1_RX, -+ .end = N_SRC_SPI1_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI1_TX, -+ .end = N_DST_SPI1_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#else -+ [1] = { -+ .start = D_SRC_SPI1_RX, -+ .end = D_SRC_SPI1_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI1_TX, -+ .end = N_DST_SPI1_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#endif -+ [3] = { -+ .start = SW_INT_IRQNO_SPI01, -+ .end = SW_INT_IRQNO_SPI01, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device sun7i_spi1_device = { -+ .name = "sun7i-spi", -+ .id = 1, -+ .num_resources = ARRAY_SIZE(sun7i_spi1_resources), -+ .resource = sun7i_spi1_resources, -+ .dev = { -+ .platform_data = &sun7i_spi1_pdata, -+ }, -+}; -+ -+static struct resource sun7i_spi2_resources[] = { -+ [0] = { -+ .start = SPI2_BASE_ADDR, -+ .end = SPI2_BASE_ADDR + 1024, -+ .flags = IORESOURCE_MEM, -+ }, -+#ifdef CONFIG_SUN7I_SPI_NDMA -+ [1] = { -+ .start = N_SRC_SPI2_RX, -+ .end = N_SRC_SPI2_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI2_TX, -+ .end = N_DST_SPI2_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#else -+ [1] = { -+ .start = D_SRC_SPI2_RX, -+ .end = D_SRC_SPI2_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI2_TX, -+ .end = N_DST_SPI2_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#endif -+ [3] = { -+ .start = SW_INT_IRQNO_SPI02, -+ .end = SW_INT_IRQNO_SPI02, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+struct sun7i_spi_platform_data sun7i_spi2_pdata = { -+ .cs_bitmap = 0x3, -+ .num_cs = 2, -+ .clk_name = "ahb_spi2", -+}; -+ -+static struct platform_device sun7i_spi2_device = { -+ .name = "sun7i-spi", -+ .id = 2, -+ .num_resources = ARRAY_SIZE(sun7i_spi2_resources), -+ .resource = sun7i_spi2_resources, -+ .dev = { -+ .platform_data = &sun7i_spi2_pdata, -+ }, -+}; -+ -+static struct resource sun7i_spi3_resources[] = { -+ [0] = { -+ .start = SPI3_BASE_ADDR, -+ .end = SPI3_BASE_ADDR + 1024, -+ .flags = IORESOURCE_MEM, -+ }, -+#ifdef CONFIG_SUN7I_SPI_NDMA -+ [1] = { -+ .start = N_SRC_SPI3_RX, -+ .end = N_SRC_SPI3_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI3_TX, -+ .end = N_DST_SPI3_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#else -+ [1] = { -+ .start = D_SRC_SPI3_RX, -+ .end = D_SRC_SPI3_RX, -+ .flags = IORESOURCE_DMA, -+ }, -+ [2] = { -+ .start = N_DST_SPI3_TX, -+ .end = N_DST_SPI3_TX, -+ .flags = IORESOURCE_DMA, -+ }, -+#endif -+ [3] = { -+ .start = SW_INT_IRQNO_SPI3, -+ .end = SW_INT_IRQNO_SPI3, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+struct sun7i_spi_platform_data sun7i_spi3_pdata = { -+ .cs_bitmap = 0x3, -+ .num_cs = 2, -+ .clk_name = "ahb_spi3", -+}; -+ -+static struct platform_device sun7i_spi3_device = { -+ .name = "sun7i-spi", -+ .id = 3, -+ .num_resources = ARRAY_SIZE(sun7i_spi3_resources), -+ .resource = sun7i_spi3_resources, -+ .dev = { -+ .platform_data = &sun7i_spi3_pdata, -+ }, -+}; -+ -+static struct spi_board_info *spi_boards = NULL; -+static int sun7i_spi_register_spidev(void) -+{ -+ int spi_dev_num = 0; -+ int ret = 0; -+ int i = 0; -+ unsigned int irq_gpio = 0; -+ char spi_board_name[32] = {0}; -+ struct spi_board_info* board; -+ -+ ret = script_parser_fetch("spi_devices", "spi_dev_num", &spi_dev_num, sizeof(int)); -+ if(ret != SCRIPT_PARSER_OK){ -+ spi_err("Get spi devices number failed\n"); -+ return -1; -+ } -+ spi_inf("Found %d spi devices in config files\n", spi_dev_num); -+ -+ /* alloc spidev board information structure */ -+ spi_boards = (struct spi_board_info*)kzalloc(sizeof(struct spi_board_info) * spi_dev_num, GFP_KERNEL); -+ if (spi_boards == NULL) -+ { -+ spi_err("Alloc spi board information failed \n"); -+ return -1; -+ } -+ -+ spi_inf("%-10s %-16s %-16s %-8s %-4s %-4s\n", "boards num", "modalias", "max_spd_hz", "bus_num", "cs", "mode"); -+ for (i=0; imodalias, sizeof(char*)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices modalias failed\n"); -+ goto fail; -+ } -+ ret = script_parser_fetch(spi_board_name, "max_speed_hz", (void*)&board->max_speed_hz, sizeof(int)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices max_speed_hz failed\n"); -+ goto fail; -+ } -+ ret = script_parser_fetch(spi_board_name, "bus_num", (void*)&board->bus_num, sizeof(u16)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices bus_num failed\n"); -+ goto fail; -+ } -+ ret = script_parser_fetch(spi_board_name, "chip_select", (void*)&board->chip_select, sizeof(u16)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices chip_select failed\n"); -+ goto fail; -+ } -+ ret = script_parser_fetch(spi_board_name, "mode", (void*)&board->mode, sizeof(u8)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices mode failed\n"); -+ goto fail; -+ } -+ ret = script_parser_fetch(spi_board_name, "irq_gpio", (void*)&irq_gpio, sizeof(unsigned int)); -+ if (ret != SCRIPT_PARSER_OK) { -+ spi_inf("%s irq gpio not used\n", spi_board_name); -+ board->irq = -1; -+ } else if(gpio_request(irq_gpio, spi_board_name) == 0) -+ board->irq = gpio_to_irq(irq_gpio); -+ /* -+ ret = script_parser_fetch(spi_board_name, "full_duplex", &board->full_duplex, sizeof(int)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices full_duplex failed\n"); -+ goto fail; -+ } -+ ret = script_parser_fetch(spi_board_name, "manual_cs", &board->manual_cs, sizeof(int)); -+ if(ret != SCRIPT_PARSER_OK) { -+ spi_err("Get spi devices manual_cs failed\n"); -+ goto fail; -+ } -+ */ -+ spi_inf("%-10d %-16s %-16d %-8d %-4d 0x%-4d\n", i, board->modalias, board->max_speed_hz, board->bus_num, board->chip_select, board->mode); -+ } -+ -+ /* register boards */ -+ ret = spi_register_board_info(spi_boards, spi_dev_num); -+ if (ret) -+ { -+ spi_err("Register board information failed\n"); -+ goto fail; -+ } -+ return 0; -+fail: -+ if (spi_boards) -+ { -+ kfree(spi_boards); -+ spi_boards = NULL; -+ } -+ return -1; -+ -+} -+ -+static int sun7i_spi_get_cfg_csbitmap(int bus_num) -+{ -+ int value = 0; -+ int ret = 0; -+ char *main_name[] = {"spi0_para", "spi1_para", "spi2_para", "spi3_para"}; -+ char *sub_name = "spi_cs_bitmap"; -+ ret = script_parser_fetch(main_name[bus_num], sub_name, &value, sizeof(int)); -+ if(ret != SCRIPT_PARSER_OK){ -+ spi_err("get spi %d para failed, err code = %d \n", bus_num, ret); -+ return 0; -+ } -+ spi_inf("bus num = %d, spi used = %d \n", bus_num, value); -+ return value; -+ -+} -+ -+#ifdef CONFIG_SUN7I_SPI_NORFLASH -+#include -+#include -+ -+/*struct mtd_partition part = { -+ .name = "p1", -+ .size = 8388608, -+ .offset = 0, -+};*/ -+ -+static struct flash_platform_data at25df641_info = { -+ .name = "m25p80", -+ .parts = NULL, -+ .nr_parts = 0, -+ .type = "at25df641", -+}; -+ -+static struct spi_board_info norflash = { -+ .modalias = "m25p80", -+ .platform_data = &at25df641_info, -+ .mode = SPI_MODE_0, -+ .irq = 0, -+ .max_speed_hz = 10 * 1000 * 1000, -+ .bus_num = 0, -+ .chip_select = 0, -+}; -+ -+static void __init sun7i_spi_norflash(void) -+{ -+ if (spi_register_board_info(&norflash, 1)) { -+ spi_err("%s: Register norflash:%s information failed\n", -+ __func__, at25df641_info.type); -+ } else { -+ spi_inf("%s: Register norflash:%s information OK\n", -+ __func__, at25df641_info.type); -+ } -+} -+#else -+static void __init sun7i_spi_norflash(void) -+{} -+#endif /* CONFIG_SUN7I_SPI_NORFLASH */ -+ -+/* get configuration in the script */ -+#define SPI0_USED_MASK 0x1 -+#define SPI1_USED_MASK 0x2 -+#define SPI2_USED_MASK 0x4 -+#define SPI3_USED_MASK 0x8 -+static int spi_used = 0; -+ -+static int __init spi_sun7i_init(void) -+{ -+ int used = 0; -+ int i = 0; -+ int ret = 0; -+ char spi_para[16] = {0}; -+ -+ spi_dbg("sw spi init !!\n"); -+ spi_used = 0; -+ for (i=0; i<4; i++) -+ { -+ used = 0; -+ sprintf(spi_para, "spi%d_para", i); -+ ret = script_parser_fetch(spi_para, "spi_used", &used, sizeof(int)); -+ if (ret) -+ { -+ spi_err("sw spi init fetch spi%d uning configuration failed\n", i); -+ continue; -+ } -+ if (used) -+ spi_used |= 1 << i; -+ } -+ -+ ret = sun7i_spi_register_spidev(); -+ if (ret) -+ { -+ spi_err("register spi devices board info failed \n"); -+ } -+ -+// sun7i_spi_norflash(); -+ -+ if (spi_used & SPI0_USED_MASK) -+ platform_device_register(&sun7i_spi0_device); -+ if (spi_used & SPI1_USED_MASK) -+ platform_device_register(&sun7i_spi1_device); -+ if (spi_used & SPI2_USED_MASK) -+ platform_device_register(&sun7i_spi2_device); -+ if (spi_used & SPI3_USED_MASK) -+ platform_device_register(&sun7i_spi3_device); -+ -+ if (spi_used) -+ { -+ return platform_driver_register(&sun7i_spi_driver); -+ } -+ else -+ { -+ pr_warning("spi: cannot find any using configuration for \ -+ all 4 spi controllers, return directly!\n"); -+ return 0; -+ } -+} -+ -+static void __exit spi_sun7i_exit(void) -+{ -+ if (spi_used) -+ platform_driver_unregister(&sun7i_spi_driver); -+} -+ -+module_init(spi_sun7i_init); -+module_exit(spi_sun7i_exit); -+ -+MODULE_AUTHOR("pannan"); -+MODULE_DESCRIPTION("SUN7I SPI BUS Driver"); -+MODULE_ALIAS("platform:sun7i-spi"); -+MODULE_LICENSE("GPL"); diff --git a/patch/kernel/sunxi-dev/banana-otg2host.patch b/patch/kernel/sunxi-dev/banana-otg2host.patch deleted file mode 100644 index 31f43e8a7c..0000000000 --- a/patch/kernel/sunxi-dev/banana-otg2host.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/arch/arm/boot/dts/sun7i-a20-bananapi.dts b/arch/arm/boot/dts/sun7i-a20-bananapi.dts -old mode 100644 -new mode 100755 -index fd7594f..817a942 ---- a/arch/arm/boot/dts/sun7i-a20-bananapi.dts -+++ b/arch/arm/boot/dts/sun7i-a20-bananapi.dts -@@ -269,7 +269,7 @@ - }; - - &usb_otg { -- dr_mode = "otg"; -+ dr_mode = "host"; - status = "okay"; - }; -