diff --git a/config/boot-odroid.ini b/config/boot-odroid.ini new file mode 100644 index 000000000..661f6b1f8 --- /dev/null +++ b/config/boot-odroid.ini @@ -0,0 +1,196 @@ +ODROIDXU-UBOOT-CONFIG + +# U-Boot Parameters +setenv initrd_high "0xffffffff" +setenv fdt_high "0xffffffff" + +# Mac address configuration +setenv macaddr "00:1e:06:61:7a:55 + +#------------------------------------------------------------------------------------------------------ +# Basic Ubuntu Setup. Don't touch unless you know what you are doing. +# -------------------------------- +setenv bootrootfs "console=tty1 console=ttySAC2,115200n8 root=/dev/mmcblk0p2 rootwait ro fsck.repair=yes" + +# boot commands +setenv bootcmd "fatload mmc 0:1 0x40008000 zImage; fatload mmc 0:1 0x44000000 dtb/exynos5422-odroidxu3.dtb; bootz 0x40008000 0x44000000" + +# --- Screen Configuration for HDMI --- # +# --------------------------------------- +# Uncomment only ONE line! Leave all commented for automatic selection. +# Uncomment only the setenv line! +# --------------------------------------- +# ODROID-VU forced resolution +# setenv videoconfig "video=HDMI-A-1:1280x800@60" +# ----------------------------------------------- +# ODROID-VU forced EDID +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/1280x800.bin" +# ----------------------------------------------- +# 1920x1080 (1080P) with monitor provided EDID information. (1080p-edid) +# setenv videoconfig "video=HDMI-A-1:1920x1080@60" +# ----------------------------------------------- +# 1920x1080 (1080P) without monitor data using generic information (1080p-noedid) +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/1920x1080.bin" +# ----------------------------------------------- +# 1920x1080 50hz (1080P) with monitor provided EDID information. (1080p 50hz-edid) +# setenv videoconfig "video=HDMI-A-1:1920x1080@50" +# ----------------------------------------------- +# 1920x1080 50hz (1080P) without monitor data using generic information (1080p 50hz-noedid) +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/1920x1080_50hz.bin" +# ----------------------------------------------- +# 1440x900 with monitor provided EDID information. +# setenv videoconfig "video=HDMI-A-1:1440x900@60" +# ----------------------------------------------- +# 1440x900 without monitor data using generic information +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/1440x900.bin" +# ----------------------------------------------- +# 1280x720 (720P) with monitor provided EDID information. (720p-edid) +# setenv videoconfig "video=HDMI-A-1:1280x720@60" +# ----------------------------------------------- +# 1280x720 (720P) without monitor data using generic information (720p-noedid) +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/1280x720.bin" +# ----------------------------------------------- +# 1024x768 without monitor data using generic information +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/1024x768.bin" +# ----------------------------------------------- +# 800x600 without monitor data using generic information +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/800x600.bin" +# ----------------------------------------------- +# 800x480 without monitor data using generic information (ODROID 7") +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/800x480.bin" +# ----------------------------------------------- +# 720x576 without monitor data using generic information +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/720x576.bin" +# ----------------------------------------------- +# 720x480 without monitor data using generic information +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/720x480.bin" +# ----------------------------------------------- +# 640x480 without monitor data using generic information +# setenv videoconfig "drm_kms_helper.edid_firmware=edid/640x480.bin" + + +# --- HDMI / DVI Mode Selection --- +# ------------------------------------------ +# - HDMI Mode +setenv vout "hdmi" +# - DVI Mode (disables sound over HDMI as per DVI compat) +# setenv vout "dvi" + + +# --- CPU Governor Setup --- +# Uncomment only one line. New governor is set after 90secs after boot. +# ------------------------------------------ +# - Performance (Keep all the CPU's at Maximum frequency) +setenv governor "performance" +# ------------------------------------------ +# - Ondemand +# setenv governor "ondemand" +# ------------------------------------------ +# - Interactive (Pretty much just like ondemand with more possible customization via sysfs.) +# setenv governor "interactive" +# ------------------------------------------ +# - Conservative (Like ondemand, but do the frequency transitions more slowly, great for battery powered applications) +# setenv governor "conservative" +# ------------------------------------------ +# - Powersave (Keeps the CPU's to the lowest possible temps) +# setenv governor "powersave" + + +#------------------------------------------------------------------------------ +# +# HDMI-PHY Parameter Control +# +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# +# HDMI Hot Plug detection +# +#------------------------------------------------------------------------------ +# +# Forces the HDMI subsystem to ignore the check if the cable is connected or +# not. +# false : disable the detection and force it as connected. +# true : let cable, board and monitor decide the connection status. +# +# default: true +# +#------------------------------------------------------------------------------ +setenv HPD "true" + +#------------------------------------------------------------------------------ +# +# TMDS data amplitude control. +# +#------------------------------------------------------------------------------ +# +# 1LSB corresponds to 20 mVdiff amplitude level. +# tx_amp_lvl : 0 = 760 mVdiff(Min), 31 = 1380 mVdiff(Max) +# +# Hardkernel default hdmi_tx_amp_lvl = 31(1380 mVdiff); +#------------------------------------------------------------------------------ +setenv hdmi_tx_amp_lvl "31" + +#------------------------------------------------------------------------------ +# +# TMDS data amplitude fine control for each channel. +# +#------------------------------------------------------------------------------ +# +# 1LSB corresponds to 20 mVdiff amplitude level. +# tx_lvl : 0 = 0 mVdiff(Min), 3 = 60 mVdiff(Max) +# +# Hardkernel default +# hdmi_tx_lvl_ch0 = 3, hdmi_tx_lvl_ch1 = 3, hdmi_tx_lvl_ch2 = 3, +#------------------------------------------------------------------------------ +setenv hdmi_tx_lvl_ch0 "3" +setenv hdmi_tx_lvl_ch1 "3" +setenv hdmi_tx_lvl_ch2 "3" + +#------------------------------------------------------------------------------ +# +# TMDS data pre-emphasis level control. +# +#------------------------------------------------------------------------------ +# +# 1LSB corresponds to -0.45dB emphasis level except for 1 +# tx_emp_lvl : 0 = 0 db(Min), 1 = -0.25 db, 2 = 0.7 db, 15 = -7.45 db(Max) +# +# Hardkernel default hdmi_tx_emp_lvl = 6 (-2.50 db); +#------------------------------------------------------------------------------ +setenv hdmi_tx_emp_lvl "6" + +#------------------------------------------------------------------------------ +# +# TMDS clock amplitude control. +# +#------------------------------------------------------------------------------ +# +# 1LSB corresponds to 20 mVdiff amplitude level. +# clk_amp_lvl : 0 = 790 mVdiff(Min), 31 = 1410 mVdiff(Max) +# +# Hardkernel default hdmi_clk_amp_lvl = 31 (1410 mVdiff) +#------------------------------------------------------------------------------ +setenv hdmi_clk_amp_lvl "31" + +#------------------------------------------------------------------------------ +# +# TMDS data source termination resistor control. +# +#------------------------------------------------------------------------------ +# +# tx_res : +# 0 = Source Termination OFF(Min), 1 = 200 ohm, 2 = 300 ohm, 3 = 120 ohm(Max) +# +# Hardkernrel default hdmi_tx_res = 0 (Source Termination OFF) +#------------------------------------------------------------------------------ +setenv hdmi_tx_res "0" + +#------------------------------------------------------------------------------ + +setenv hdmi_phy_control "hdmi_tx_amp_lvl=${hdmi_tx_amp_lvl} hdmi_tx_lvl_ch0=${hdmi_tx_lvl_ch0} hdmi_tx_lvl_ch1=${hdmi_tx_lvl_ch1} hdmi_tx_lvl_ch2=${hdmi_tx_lvl_ch2} hdmi_tx_emp_lvl=${hdmi_tx_emp_lvl} hdmi_clk_amp_lvl=${hdmi_clk_amp_lvl} hdmi_tx_res=${hdmi_tx_res} HPD=${HPD} vout=${vout}" + +# final boot args +setenv bootargs "${bootrootfs} ${videoconfig} smsc95xx.macaddr=${macaddr} governor=${governor} ${hdmi_phy_control}" +# drm.debug=0xff +# Boot the board +boot diff --git a/config/linux-odroid-next.config b/config/linux-odroid-next.config new file mode 100644 index 000000000..379164d61 --- /dev/null +++ b/config/linux-odroid-next.config @@ -0,0 +1,4114 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 4.2.6 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_MIGHT_HAVE_PCI=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_NO_IOPORT_MAP=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_ARCH_HAS_BANDGAP=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_FHANDLE=y +CONFIG_USELIB=y +CONFIG_AUDIT=y +CONFIG_HAVE_ARCH_AUDITSYSCALL=y +# CONFIG_AUDITSYSCALL is not set + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_HANDLE_DOMAIN_IRQ=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +# CONFIG_NO_HZ_FULL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y + +# +# RCU Subsystem +# +CONFIG_PREEMPT_RCU=y +# CONFIG_RCU_EXPERT is not set +CONFIG_SRCU=y +# CONFIG_TASKS_RCU is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_NOCB_CPU is not set +# CONFIG_RCU_EXPEDITE_BOOT is not set +CONFIG_BUILD_BIN2C=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_PAGE_COUNTER=y +CONFIG_MEMCG=y +# CONFIG_MEMCG_SWAP is not set +CONFIG_MEMCG_KMEM=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_CGROUP_WRITEBACK=y +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +# CONFIG_USER_NS is not set +CONFIG_PID_NS=y +CONFIG_NET_NS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_BPF=y +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_MULTIUSER=y +# CONFIG_SGETMASK_SYSCALL is not set +CONFIG_SYSFS_SYSCALL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_BPF_SYSCALL is not set +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ADVISE_SYSCALLS=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_UPROBES is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_CC_STACKPROTECTOR=y +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_CC_STACKPROTECTOR_NONE=y +# CONFIG_CC_STACKPROTECTOR_REGULAR is not set +# CONFIG_CC_STACKPROTECTOR_STRONG is not set +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_COMPRESS is not set +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set +# CONFIG_BLK_DEV_THROTTLING is not set +# CONFIG_BLK_CMDLINE_PARSER is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_AIX_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set +# CONFIG_CMDLINE_PARTITION is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_CFQ_GROUP_IOSCHED is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_ASN1=y +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_SHMOBILE_LEGACY is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_DIGICOLOR is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MEDIATEK is not set + +# +# TI OMAP/AM/DM/DRA Family +# +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_S5PV210 is not set +CONFIG_ARCH_EXYNOS=y +CONFIG_ARCH_EXYNOS3=y +CONFIG_ARCH_EXYNOS4=y +CONFIG_ARCH_EXYNOS5=y + +# +# EXYNOS SoCs +# +CONFIG_SOC_EXYNOS3250=y +CONFIG_CPU_EXYNOS4210=y +CONFIG_SOC_EXYNOS4212=y +CONFIG_SOC_EXYNOS4412=y +CONFIG_SOC_EXYNOS4415=y +CONFIG_SOC_EXYNOS5250=y +CONFIG_SOC_EXYNOS5260=y +CONFIG_SOC_EXYNOS5410=y +CONFIG_SOC_EXYNOS5420=y +CONFIG_SOC_EXYNOS5440=y +CONFIG_SOC_EXYNOS5800=y +CONFIG_EXYNOS5420_MCPM=y +CONFIG_EXYNOS_CPU_SUSPEND=y +CONFIG_PLAT_SAMSUNG=y + +# +# Samsung Common options +# + +# +# Boot options +# +CONFIG_S5P_DEV_MFC=y + +# +# Power management +# +# CONFIG_SAMSUNG_PM_CHECK is not set +# CONFIG_ARCH_SHMOBILE_MULTI is not set +# CONFIG_ARCH_SUNXI is not set +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_ARCH_ZYNQ is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_VIRT_EXT=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_VDSO=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_PL310_ERRATA_769419 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +# CONFIG_ARM_KERNMEM_PERMS is not set +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +CONFIG_ARM_ERRATA_643719=y +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_773022 is not set + +# +# Bus support +# +# CONFIG_PCI is not set +# CONFIG_PCI_DOMAINS_GENERIC is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +# CONFIG_SCHED_MC is not set +# CONFIG_SCHED_SMT is not set +CONFIG_HAVE_ARM_SCU=y +CONFIG_HAVE_ARM_ARCH_TIMER=y +CONFIG_MCPM=y +CONFIG_BIG_LITTLE=y +# CONFIG_BL_SWITCHER is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=8 +CONFIG_HOTPLUG_CPU=y +CONFIG_ARM_PSCI=y +CONFIG_ARCH_NR_GPIO=512 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ_FIXED=200 +CONFIG_HZ=200 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +# CONFIG_ARM_MODULE_PLTS is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_NO_BOOTMEM=y +CONFIG_MEMORY_ISOLATION=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +# CONFIG_CLEANCACHE is not set +# CONFIG_FRONTSWAP is not set +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_AREAS=7 +# CONFIG_ZPOOL is not set +# CONFIG_ZBUD is not set +# CONFIG_ZSMALLOC is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +CONFIG_SECCOMP=y +CONFIG_SWIOTLB=y +CONFIG_IOMMU_HELPER=y +# CONFIG_XEN is not set + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +CONFIG_CMDLINE="root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc mem=256M" +CONFIG_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_CMDLINE_EXTEND is not set +# CONFIG_CMDLINE_FORCE is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_BOOST_SW=y +CONFIG_CPU_FREQ_STAT=y +CONFIG_CPU_FREQ_STAT_DETAILS=y +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m + +# +# CPU frequency scaling drivers +# +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_BIG_LITTLE_CPUFREQ=y +CONFIG_ARM_DT_BL_CPUFREQ=y +# CONFIG_ARM_EXYNOS_CPUFREQ is not set +CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW=y +# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +# CONFIG_QORIQ_CPUFREQ is not set + +# +# CPU Idle +# +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y + +# +# ARM CPU Idle Drivers +# +# CONFIG_ARM_CPUIDLE is not set +# CONFIG_ARM_BIG_LITTLE_CPUIDLE is not set +# CONFIG_ARM_HIGHBANK_CPUIDLE is not set +CONFIG_ARM_EXYNOS_CPUIDLE=y +CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +# CONFIG_KERNEL_MODE_NEON is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +CONFIG_BINFMT_MISC=m +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_HIBERNATION is not set +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=100 +CONFIG_PM_WAKELOCKS_GC=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +# CONFIG_APM_EMULATION is not set +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_PM_GENERIC_DOMAINS_SLEEP=y +CONFIG_PM_GENERIC_DOMAINS_OF=y +CONFIG_CPU_PM=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_NET=y +CONFIG_NET_INGRESS=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_ALGO=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=m +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_IPVTI is not set +# CONFIG_NET_UDP_TUNNEL is not set +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +# CONFIG_GENEVE_CORE is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +CONFIG_INET_TUNNEL=m +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=m +# CONFIG_IPV6_ROUTER_PREF is not set +# CONFIG_IPV6_OPTIMISTIC_DAD is not set +# CONFIG_INET6_AH is not set +# CONFIG_INET6_ESP is not set +# CONFIG_INET6_IPCOMP is not set +# CONFIG_IPV6_MIP6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +# CONFIG_IPV6_VTI is not set +CONFIG_IPV6_SIT=m +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +# CONFIG_IPV6_TUNNEL is not set +# CONFIG_IPV6_GRE is not set +# CONFIG_IPV6_MULTIPLE_TABLES is not set +# CONFIG_IPV6_MROUTE is not set +CONFIG_NETLABEL=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NET_PTP_CLASSIFY is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_INGRESS=y +# CONFIG_NETFILTER_NETLINK_ACCT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +CONFIG_NF_CONNTRACK=m +# CONFIG_NF_CONNTRACK_MARK is not set +CONFIG_NF_CONNTRACK_PROCFS=y +# CONFIG_NF_CONNTRACK_EVENTS is not set +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +# CONFIG_NF_CT_PROTO_DCCP is not set +# CONFIG_NF_CT_PROTO_SCTP is not set +# CONFIG_NF_CT_PROTO_UDPLITE is not set +# CONFIG_NF_CONNTRACK_AMANDA is not set +# CONFIG_NF_CONNTRACK_FTP is not set +# CONFIG_NF_CONNTRACK_H323 is not set +# CONFIG_NF_CONNTRACK_IRC is not set +# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set +# CONFIG_NF_CONNTRACK_SNMP is not set +# CONFIG_NF_CONNTRACK_PPTP is not set +# CONFIG_NF_CONNTRACK_SANE is not set +# CONFIG_NF_CONNTRACK_SIP is not set +# CONFIG_NF_CONNTRACK_TFTP is not set +# CONFIG_NF_CT_NETLINK is not set +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +# CONFIG_NF_NAT_AMANDA is not set +# CONFIG_NF_NAT_FTP is not set +# CONFIG_NF_NAT_IRC is not set +# CONFIG_NF_NAT_SIP is not set +# CONFIG_NF_NAT_TFTP is not set +# CONFIG_NF_NAT_REDIRECT is not set +# CONFIG_NF_TABLES is not set +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +# CONFIG_NETFILTER_XT_MARK is not set +# CONFIG_NETFILTER_XT_CONNMARK is not set + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set +# CONFIG_NETFILTER_XT_TARGET_CONNMARK is not set +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set +# CONFIG_NETFILTER_XT_TARGET_LED is not set +# CONFIG_NETFILTER_XT_TARGET_LOG is not set +# CONFIG_NETFILTER_XT_TARGET_MARK is not set +CONFIG_NETFILTER_XT_NAT=m +# CONFIG_NETFILTER_XT_TARGET_NETMAP is not set +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set +# CONFIG_NETFILTER_XT_TARGET_REDIRECT is not set +# CONFIG_NETFILTER_XT_TARGET_TEE is not set +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set + +# +# Xtables matches +# +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +# CONFIG_NETFILTER_XT_MATCH_BPF is not set +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_CLUSTER is not set +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNBYTES is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLABEL is not set +# CONFIG_NETFILTER_XT_MATCH_CONNLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_CONNMARK is not set +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +# CONFIG_NETFILTER_XT_MATCH_CPU is not set +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set +# CONFIG_NETFILTER_XT_MATCH_ECN is not set +# CONFIG_NETFILTER_XT_MATCH_ESP is not set +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_HELPER is not set +# CONFIG_NETFILTER_XT_MATCH_HL is not set +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set +# CONFIG_NETFILTER_XT_MATCH_MAC is not set +# CONFIG_NETFILTER_XT_MATCH_MARK is not set +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set +# CONFIG_NETFILTER_XT_MATCH_REALM is not set +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set +# CONFIG_NETFILTER_XT_MATCH_STATE is not set +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set +# CONFIG_NETFILTER_XT_MATCH_STRING is not set +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set +# CONFIG_NETFILTER_XT_MATCH_TIME is not set +# CONFIG_NETFILTER_XT_MATCH_U32 is not set +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +# CONFIG_NF_LOG_ARP is not set +# CONFIG_NF_LOG_IPV4 is not set +# CONFIG_NF_REJECT_IPV4 is not set +CONFIG_NF_NAT_IPV4=m +CONFIG_NF_NAT_MASQUERADE_IPV4=m +# CONFIG_NF_NAT_PPTP is not set +# CONFIG_NF_NAT_H323 is not set +CONFIG_IP_NF_IPTABLES=y +# CONFIG_IP_NF_MATCH_AH is not set +# CONFIG_IP_NF_MATCH_ECN is not set +# CONFIG_IP_NF_MATCH_TTL is not set +# CONFIG_IP_NF_FILTER is not set +# CONFIG_IP_NF_TARGET_SYNPROXY is not set +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +# CONFIG_IP_NF_TARGET_NETMAP is not set +# CONFIG_IP_NF_TARGET_REDIRECT is not set +# CONFIG_IP_NF_MANGLE is not set +# CONFIG_IP_NF_RAW is not set +# CONFIG_IP_NF_SECURITY is not set +# CONFIG_IP_NF_ARPTABLES is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_NF_DEFRAG_IPV6 is not set +# CONFIG_NF_CONNTRACK_IPV6 is not set +# CONFIG_NF_REJECT_IPV6 is not set +# CONFIG_NF_LOG_IPV6 is not set +# CONFIG_IP6_NF_IPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_L2TP is not set +# CONFIG_BRIDGE is not set +CONFIG_HAVE_NET_DSA=y +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_6LOWPAN is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set +# CONFIG_DNS_RESOLVER is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +# CONFIG_MPLS is not set +# CONFIG_HSR is not set +# CONFIG_NET_SWITCHDEV is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_CGROUP_NET_PRIO is not set +# CONFIG_CGROUP_NET_CLASSID is not set +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=m +CONFIG_BT_BREDR=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +CONFIG_BT_LE=y +# CONFIG_BT_SELFTEST is not set +# CONFIG_BT_DEBUGFS is not set + +# +# Bluetooth device drivers +# +CONFIG_BT_INTEL=m +CONFIG_BT_BCM=m +CONFIG_BT_RTL=m +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_BCM=y +CONFIG_BT_HCIBTUSB_RTL=y +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_3WIRE=y +# CONFIG_BT_HCIUART_INTEL is not set +# CONFIG_BT_HCIUART_BCM is not set +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=m +# CONFIG_NL80211_TESTMODE is not set +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +# CONFIG_CFG80211_WEXT is not set +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +# CONFIG_MAC80211_RC_MINSTREL_VHT is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_MAC80211_MESH is not set +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +CONFIG_RFKILL_REGULATOR=y +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# +CONFIG_ARM_AMBA=y +# CONFIG_TEGRA_AHB is not set + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set +CONFIG_ALLOW_DEV_COREDUMP=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_FENCE_TRACE is not set +CONFIG_DMA_CMA=y + +# +# Default contiguous memory area size: +# +CONFIG_CMA_SIZE_MBYTES=128 +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_ALIGNMENT=8 + +# +# Bus devices +# +CONFIG_ARM_CCI=y +CONFIG_ARM_CCI_PMU=y +CONFIG_ARM_CCI400_COMMON=y +# CONFIG_ARM_CCI400_PMU is not set +CONFIG_ARM_CCI400_PORT_CTRL=y +CONFIG_ARM_CCI500_PMU=y +# CONFIG_ARM_CCN is not set +# CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +CONFIG_DTC=y +CONFIG_OF=y +# CONFIG_OF_UNITTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +CONFIG_OF_RESERVED_MEM=y +# CONFIG_OF_OVERLAY is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +CONFIG_BLK_DEV_CRYPTOLOOP=y +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +CONFIG_SRAM=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# Intel MIC Bus Driver +# + +# +# SCIF Bus Driver +# + +# +# Intel MIC Host Driver +# + +# +# Intel MIC Card Driver +# + +# +# SCIF Driver +# +# CONFIG_ECHO is not set +# CONFIG_CXL_BASE is not set +# CONFIG_CXL_KERNEL_API is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +CONFIG_SCSI_SCAN_ASYNC=y + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BCACHE is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_MQ_DEFAULT is not set +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_CACHE is not set +# CONFIG_DM_ERA is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_UEVENT is not set +# CONFIG_DM_FLAKEY is not set +# CONFIG_DM_VERITY is not set +# CONFIG_DM_SWITCH is not set +# CONFIG_DM_LOG_WRITES is not set +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_MII=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_NET_TEAM is not set +CONFIG_MACVLAN=m +# CONFIG_MACVTAP is not set +# CONFIG_IPVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=m +# CONFIG_TUN_VNET_CROSS_LE is not set +CONFIG_VETH=m +# CONFIG_NLMON is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +CONFIG_ETHERNET=y +# CONFIG_ALTERA_TSE is not set +CONFIG_NET_VENDOR_ARC=y +# CONFIG_ARC_EMAC is not set +# CONFIG_EMAC_ROCKCHIP is not set +CONFIG_NET_CADENCE=y +# CONFIG_MACB is not set +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_BCMGENET is not set +# CONFIG_SYSTEMPORT is not set +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_EZCHIP=y +# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_HISILICON=y +# CONFIG_HIX5HD2_GMAC is not set +# CONFIG_HIP04_ETH is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MARVELL=y +# CONFIG_MVMDIO is not set +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NET_VENDOR_MICROCHIP=y +# CONFIG_ENC28J60 is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_QUALCOMM=y +# CONFIG_QCA7000 is not set +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +CONFIG_NET_VENDOR_SAMSUNG=y +# CONFIG_SXGBE_ETH is not set +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SMSC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC911X is not set +CONFIG_SMSC911X=y +# CONFIG_SMSC911X_ARCH_HOOKS is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NET_VENDOR_VIA=y +# CONFIG_VIA_RHINE is not set +# CONFIG_VIA_VELOCITY is not set +CONFIG_NET_VENDOR_WIZNET=y +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM7XXX_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_DP83867_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +CONFIG_USB_NET_DRIVERS=m +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +# CONFIG_USB_NET_CDC_EEM is not set +CONFIG_USB_NET_CDC_NCM=m +# CONFIG_USB_NET_HUAWEI_CDC_NCM is not set +# CONFIG_USB_NET_CDC_MBIM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SR9700 is not set +# CONFIG_USB_NET_SR9800 is not set +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_CDC_SUBSET=m +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=m +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_NET_INT51X1 is not set +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_WLAN=y +CONFIG_LIBERTAS_THINFIRM=m +CONFIG_LIBERTAS_THINFIRM_DEBUG=y +CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_AT76C50X_USB=m +CONFIG_USB_ZD1201=m +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_RTL8187=m +CONFIG_RTL8187_LEDS=y +# CONFIG_MAC80211_HWSIM is not set +CONFIG_ATH_CARDS=m +# CONFIG_ATH_DEBUG is not set +# CONFIG_ATH9K is not set +# CONFIG_ATH9K_HTC is not set +# CONFIG_CARL9170 is not set +CONFIG_ATH6KL=m +# CONFIG_ATH6KL_SDIO is not set +CONFIG_ATH6KL_USB=m +# CONFIG_ATH6KL_DEBUG is not set +# CONFIG_AR5523 is not set +# CONFIG_ATH10K is not set +# CONFIG_WCN36XX is not set +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +CONFIG_BRCMUTIL=m +# CONFIG_BRCMSMAC is not set +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_PROTO_BCDC=y +# CONFIG_BRCMFMAC_SDIO is not set +CONFIG_BRCMFMAC_USB=y +# CONFIG_BRCM_TRACING is not set +# CONFIG_BRCMDBG is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_P54_COMMON is not set +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RT2800_LIB=m +CONFIG_RT2X00_LIB_USB=m +CONFIG_RT2X00_LIB=m +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_LEDS=y +# CONFIG_RT2X00_DEBUG is not set +CONFIG_WL_MEDIATEK=y +CONFIG_MT7601U=m +CONFIG_RTL_CARDS=m +CONFIG_RTL8192CU=m +CONFIG_RTLWIFI=m +CONFIG_RTLWIFI_USB=m +CONFIG_RTLWIFI_DEBUG=y +CONFIG_RTL8192C_COMMON=m +# CONFIG_WL_TI is not set +# CONFIG_ZD1211RW is not set +# CONFIG_MWIFIEX is not set +# CONFIG_CW1200 is not set +# CONFIG_RSI_91X is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_LEDS=m +CONFIG_INPUT_FF_MEMLESS=m +CONFIG_INPUT_POLLDEV=m +CONFIG_INPUT_SPARSEKMAP=m +CONFIG_INPUT_MATRIXKMAP=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=m +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_JOYDEV=m +CONFIG_INPUT_EVDEV=m +CONFIG_INPUT_EVBUG=m + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ADP5588=m +CONFIG_KEYBOARD_ADP5589=m +CONFIG_KEYBOARD_ATKBD=m +CONFIG_KEYBOARD_QT1070=m +CONFIG_KEYBOARD_QT2160=m +CONFIG_KEYBOARD_LKKBD=m +CONFIG_KEYBOARD_GPIO=m +CONFIG_KEYBOARD_GPIO_POLLED=m +CONFIG_KEYBOARD_TCA6416=m +CONFIG_KEYBOARD_TCA8418=m +CONFIG_KEYBOARD_MATRIX=m +CONFIG_KEYBOARD_LM8323=m +CONFIG_KEYBOARD_LM8333=m +CONFIG_KEYBOARD_MAX7359=m +CONFIG_KEYBOARD_MCS=m +CONFIG_KEYBOARD_MPR121=m +CONFIG_KEYBOARD_NEWTON=m +CONFIG_KEYBOARD_OPENCORES=m +CONFIG_KEYBOARD_SAMSUNG=y +CONFIG_KEYBOARD_STOWAWAY=m +CONFIG_KEYBOARD_SUNKBD=m +CONFIG_KEYBOARD_OMAP4=m +CONFIG_KEYBOARD_XTKBD=m +CONFIG_KEYBOARD_CROS_EC=m +CONFIG_KEYBOARD_CAP11XX=m +CONFIG_KEYBOARD_BCM=m +CONFIG_INPUT_MOUSE=y +# CONFIG_MOUSE_PS2 is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +CONFIG_MOUSE_CYAPA=m +CONFIG_MOUSE_ELAN_I2C=m +CONFIG_MOUSE_ELAN_I2C_I2C=y +CONFIG_MOUSE_ELAN_I2C_SMBUS=y +CONFIG_MOUSE_VSXXXAA=m +CONFIG_MOUSE_GPIO=m +CONFIG_MOUSE_SYNAPTICS_I2C=m +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_ZHENHUA=m +CONFIG_JOYSTICK_AS5011=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=m +CONFIG_TABLET_USB_AIPTEK=m +CONFIG_TABLET_USB_GTCO=m +CONFIG_TABLET_USB_HANWANG=m +CONFIG_TABLET_USB_KBTAB=m +CONFIG_TABLET_SERIAL_WACOM4=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_OF_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=m +CONFIG_TOUCHSCREEN_AD7877=m +CONFIG_TOUCHSCREEN_AD7879=m +CONFIG_TOUCHSCREEN_AD7879_I2C=m +CONFIG_TOUCHSCREEN_AD7879_SPI=m +CONFIG_TOUCHSCREEN_AR1021_I2C=m +CONFIG_TOUCHSCREEN_ATMEL_MXT=m +CONFIG_TOUCHSCREEN_AUO_PIXCIR=m +CONFIG_TOUCHSCREEN_BU21013=m +CONFIG_TOUCHSCREEN_CHIPONE_ICN8318=m +CONFIG_TOUCHSCREEN_CY8CTMG110=m +CONFIG_TOUCHSCREEN_CYTTSP_CORE=m +CONFIG_TOUCHSCREEN_CYTTSP_I2C=m +CONFIG_TOUCHSCREEN_CYTTSP_SPI=m +CONFIG_TOUCHSCREEN_CYTTSP4_CORE=m +CONFIG_TOUCHSCREEN_CYTTSP4_I2C=m +CONFIG_TOUCHSCREEN_CYTTSP4_SPI=m +CONFIG_TOUCHSCREEN_DYNAPRO=m +CONFIG_TOUCHSCREEN_HAMPSHIRE=m +CONFIG_TOUCHSCREEN_EETI=m +CONFIG_TOUCHSCREEN_EGALAX=m +CONFIG_TOUCHSCREEN_FUJITSU=m +CONFIG_TOUCHSCREEN_GOODIX=m +CONFIG_TOUCHSCREEN_ILI210X=m +CONFIG_TOUCHSCREEN_GUNZE=m +CONFIG_TOUCHSCREEN_ELAN=m +CONFIG_TOUCHSCREEN_ELO=m +CONFIG_TOUCHSCREEN_WACOM_W8001=m +CONFIG_TOUCHSCREEN_WACOM_I2C=m +CONFIG_TOUCHSCREEN_MAX11801=m +CONFIG_TOUCHSCREEN_MCS5000=m +CONFIG_TOUCHSCREEN_MMS114=m +CONFIG_TOUCHSCREEN_MTOUCH=m +CONFIG_TOUCHSCREEN_INEXIO=m +CONFIG_TOUCHSCREEN_MK712=m +CONFIG_TOUCHSCREEN_PENMOUNT=m +CONFIG_TOUCHSCREEN_EDT_FT5X06=m +CONFIG_TOUCHSCREEN_TOUCHRIGHT=m +CONFIG_TOUCHSCREEN_TOUCHWIN=m +CONFIG_TOUCHSCREEN_PIXCIR=m +CONFIG_TOUCHSCREEN_WDT87XX_I2C=m +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_TOUCHSCREEN_USB_EGALAX=y +CONFIG_TOUCHSCREEN_USB_PANJIT=y +CONFIG_TOUCHSCREEN_USB_3M=y +CONFIG_TOUCHSCREEN_USB_ITM=y +CONFIG_TOUCHSCREEN_USB_ETURBO=y +CONFIG_TOUCHSCREEN_USB_GUNZE=y +CONFIG_TOUCHSCREEN_USB_DMC_TSC10=y +CONFIG_TOUCHSCREEN_USB_IRTOUCH=y +CONFIG_TOUCHSCREEN_USB_IDEALTEK=y +CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH=y +CONFIG_TOUCHSCREEN_USB_GOTOP=y +CONFIG_TOUCHSCREEN_USB_JASTEC=y +CONFIG_TOUCHSCREEN_USB_ELO=y +CONFIG_TOUCHSCREEN_USB_E2I=y +CONFIG_TOUCHSCREEN_USB_ZYTRONIC=y +CONFIG_TOUCHSCREEN_USB_ETT_TC45USB=y +CONFIG_TOUCHSCREEN_USB_NEXIO=y +CONFIG_TOUCHSCREEN_USB_EASYTOUCH=y +CONFIG_TOUCHSCREEN_TOUCHIT213=m +CONFIG_TOUCHSCREEN_TSC_SERIO=m +CONFIG_TOUCHSCREEN_TSC2005=m +CONFIG_TOUCHSCREEN_TSC2007=m +CONFIG_TOUCHSCREEN_ST1232=m +CONFIG_TOUCHSCREEN_SX8654=m +CONFIG_TOUCHSCREEN_TPS6507X=m +CONFIG_TOUCHSCREEN_ZFORCE=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_AD714X=m +CONFIG_INPUT_AD714X_I2C=m +CONFIG_INPUT_AD714X_SPI=m +CONFIG_INPUT_BMA150=m +CONFIG_INPUT_E3X0_BUTTON=m +CONFIG_INPUT_MMA8450=m +CONFIG_INPUT_MPU3050=m +CONFIG_INPUT_GP2A=m +CONFIG_INPUT_GPIO_BEEPER=m +CONFIG_INPUT_GPIO_TILT_POLLED=m +CONFIG_INPUT_ATI_REMOTE2=m +CONFIG_INPUT_KEYSPAN_REMOTE=m +CONFIG_INPUT_KXTJ9=m +CONFIG_INPUT_KXTJ9_POLLED_MODE=y +CONFIG_INPUT_POWERMATE=m +CONFIG_INPUT_YEALINK=m +CONFIG_INPUT_CM109=m +CONFIG_INPUT_REGULATOR_HAPTIC=m +CONFIG_INPUT_UINPUT=m +CONFIG_INPUT_PCF8574=m +CONFIG_INPUT_PWM_BEEPER=m +CONFIG_INPUT_GPIO_ROTARY_ENCODER=m +CONFIG_INPUT_ADXL34X=m +CONFIG_INPUT_ADXL34X_I2C=m +CONFIG_INPUT_ADXL34X_SPI=m +CONFIG_INPUT_IMS_PCU=m +CONFIG_INPUT_CMA3000=m +CONFIG_INPUT_CMA3000_I2C=m +CONFIG_INPUT_SOC_BUTTON_ARRAY=m +CONFIG_INPUT_DRV260X_HAPTICS=m +CONFIG_INPUT_DRV2665_HAPTICS=m +CONFIG_INPUT_DRV2667_HAPTICS=m + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=m +CONFIG_SERIO_AMBAKMI=m +CONFIG_SERIO_LIBPS2=y +CONFIG_SERIO_RAW=m +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_APBPS2 is not set +CONFIG_GAMEPORT=m +# CONFIG_GAMEPORT_NS558 is not set +# CONFIG_GAMEPORT_L4 is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +# CONFIG_SERIAL_8250_CONSOLE is not set +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_EM is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +# CONFIG_SERIAL_AMBA_PL011 is not set +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +CONFIG_SERIAL_SAMSUNG=y +CONFIG_SERIAL_SAMSUNG_UARTS_4=y +CONFIG_SERIAL_SAMSUNG_UARTS=4 +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_BCM63XX is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_FSL_LPUART is not set +# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set +# CONFIG_SERIAL_ST_ASC is not set +# CONFIG_SERIAL_STM32 is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +CONFIG_HW_RANDOM_EXYNOS=y +CONFIG_HW_RANDOM_TPM=y +# CONFIG_R3964 is not set +CONFIG_RAW_DRIVER=m +CONFIG_MAX_RAW_DEVS=256 +CONFIG_TCG_TPM=y +# CONFIG_TCG_TIS_I2C_ATMEL is not set +CONFIG_TCG_TIS_I2C_INFINEON=y +# CONFIG_TCG_TIS_I2C_NUVOTON is not set +# CONFIG_TCG_TIS_ST33ZP24 is not set +# CONFIG_XILLYBUS is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_MUX=m + +# +# Multiplexer I2C Chip support +# +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +# CONFIG_I2C_MUX_GPIO is not set +# CONFIG_I2C_MUX_PCA9541 is not set +# CONFIG_I2C_MUX_PCA954x is not set +# CONFIG_I2C_MUX_PINCTRL is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +CONFIG_I2C_EXYNOS5=y +CONFIG_I2C_GPIO=y +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_RK3X is not set +CONFIG_HAVE_S3C2410_I2C=y +CONFIG_I2C_S3C2410=y +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +CONFIG_I2C_CROS_EC_TUNNEL=y +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_SLAVE is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PL022 is not set +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_ROCKCHIP is not set +CONFIG_SPI_S3C64XX=m +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_ZYNQMP_GQSPI is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINCTRL_AMD is not set +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PINCTRL_SAMSUNG=y +CONFIG_PINCTRL_EXYNOS=y +CONFIG_PINCTRL_EXYNOS5440=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_OF_GPIO=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers +# +# CONFIG_GPIO_74XX_MMIO is not set +# CONFIG_GPIO_ALTERA is not set +# CONFIG_GPIO_DWAPB is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_PL061 is not set +# CONFIG_GPIO_SCH311X is not set +# CONFIG_GPIO_SYSCON is not set +# CONFIG_GPIO_ZEVIO is not set + +# +# I2C GPIO expanders +# +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set + +# +# MFD GPIO expanders +# + +# +# SPI GPIO expanders +# +# CONFIG_GPIO_74X164 is not set +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# USB GPIO expanders +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +CONFIG_BATTERY_SBS=y +# CONFIG_BATTERY_BQ27x00 is not set +CONFIG_BATTERY_MAX17040=y +CONFIG_BATTERY_MAX17042=y +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set +# CONFIG_CHARGER_BQ24735 is not set +# CONFIG_CHARGER_BQ25890 is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set +# CONFIG_CHARGER_RT9455 is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_AVS is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7310 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_IIO_HWMON is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_HTU21 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +CONFIG_SENSORS_LM90=y +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT7802 is not set +# CONFIG_SENSORS_NCT7904 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +CONFIG_SENSORS_PWM_FAN=y +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +# CONFIG_THERMAL_WRITABLE_TRIPS is not set +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +CONFIG_CPU_THERMAL=y +# CONFIG_CLOCK_THERMAL is not set +CONFIG_THERMAL_EMULATION=y +# CONFIG_IMX_THERMAL is not set + +# +# Texas Instruments thermal drivers +# +# CONFIG_TI_SOC_THERMAL is not set + +# +# Samsung thermal drivers +# +CONFIG_EXYNOS_THERMAL=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_GPIO_WATCHDOG is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +# CONFIG_CADENCE_WATCHDOG is not set +CONFIG_HAVE_S3C2410_WATCHDOG=y +CONFIG_S3C2410_WATCHDOG=y +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_MEN_A21_WDT is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_ATMEL_HLCDC is not set +# CONFIG_MFD_BCM590XX is not set +# CONFIG_MFD_AXP20X is not set +CONFIG_MFD_CROS_EC=m +CONFIG_MFD_CROS_EC_I2C=m +CONFIG_MFD_CROS_EC_SPI=m +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_DA9063 is not set +# CONFIG_MFD_DA9150 is not set +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_HI6421_PMIC is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_MFD_MT6397 is not set +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_RT5033 is not set +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RK808 is not set +# CONFIG_MFD_RN5T618 is not set +CONFIG_MFD_SEC_CORE=y +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SKY81452 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_STMPE is not set +CONFIG_MFD_SYSCON=y +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_DEBUG=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_FAN53555 is not set +CONFIG_REGULATOR_GPIO=y +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_PFUZE100 is not set +# CONFIG_REGULATOR_PWM is not set +CONFIG_REGULATOR_S2MPA01=y +CONFIG_REGULATOR_S2MPS11=y +CONFIG_REGULATOR_S5M8767=y +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=m + +# +# Multimedia core support +# +# CONFIG_MEDIA_CAMERA_SUPPORT is not set +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_SDR_SUPPORT is not set +CONFIG_MEDIA_RC_SUPPORT=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_TTPCI_EEPROM is not set + +# +# Media drivers +# +CONFIG_RC_CORE=m +CONFIG_RC_MAP=m +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_NEC_DECODER=m +CONFIG_IR_RC5_DECODER=m +CONFIG_IR_RC6_DECODER=m +CONFIG_IR_JVC_DECODER=m +CONFIG_IR_SONY_DECODER=m +CONFIG_IR_SANYO_DECODER=m +CONFIG_IR_SHARP_DECODER=m +CONFIG_IR_MCE_KBD_DECODER=m +CONFIG_IR_XMP_DECODER=m +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_HIX5HD2=m +CONFIG_IR_IMON=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_IGORPLUGUSB=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_GPIO_CIR=m +CONFIG_IR_GPIOPLUG_CIR=m +# CONFIG_MEDIA_USB_SUPPORT is not set + +# +# Supported MMC/SDIO adapters +# +# CONFIG_CYPRESS_FIRMWARE is not set + +# +# Media ancillary drivers (tuners, sensors, i2c, frontends) +# + +# +# Customise DVB Frontends +# +CONFIG_DVB_TUNER_DIB0070=m +CONFIG_DVB_TUNER_DIB0090=m + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set + +# +# Graphics support +# + +# +# Direct Rendering Manager +# +CONFIG_DRM=y +CONFIG_DRM_MIPI_DSI=y +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_KMS_FB_HELPER=y +# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set + +# +# I2C encoder or helper chips +# +# CONFIG_DRM_I2C_ADV7511 is not set +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +CONFIG_DRM_PTN3460=y +CONFIG_DRM_PS8622=y +# CONFIG_DRM_VGEM is not set +CONFIG_DRM_EXYNOS=y +CONFIG_DRM_EXYNOS_FIMD=y +# CONFIG_DRM_EXYNOS5433_DECON is not set +# CONFIG_DRM_EXYNOS7_DECON is not set +# CONFIG_DRM_EXYNOS_DPI is not set +CONFIG_DRM_EXYNOS_DSI=y +CONFIG_DRM_EXYNOS_DP=y +CONFIG_DRM_EXYNOS_HDMI=y +# CONFIG_DRM_EXYNOS_VIDI is not set +# CONFIG_DRM_EXYNOS_G2D is not set +# CONFIG_DRM_EXYNOS_IPP is not set +# CONFIG_DRM_UDL is not set +# CONFIG_DRM_ARMADA is not set +# CONFIG_DRM_TILCDC is not set +CONFIG_DRM_PANEL=y + +# +# Display Panels +# +CONFIG_DRM_PANEL_SIMPLE=y +# CONFIG_DRM_PANEL_LD9040 is not set +CONFIG_DRM_PANEL_S6E8AA0=y +# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set +# CONFIG_DRM_STI is not set + +# +# Frame buffer Devices +# +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +CONFIG_FB_CMDLINE=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +CONFIG_FB_SYS_FILLRECT=m +CONFIG_FB_SYS_COPYAREA=m +CONFIG_FB_SYS_IMAGEBLIT=m +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=m +CONFIG_FB_DEFERRED_IO=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +CONFIG_FB_BACKLIGHT=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_ARMCLCD is not set +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_S3C is not set +# CONFIG_FB_SMSCUFX is not set +CONFIG_FB_UDL=m +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +CONFIG_FB_SIMPLE=y +CONFIG_EXYNOS_VIDEO=y +CONFIG_EXYNOS_MIPI_DSI=y +# CONFIG_EXYNOS_LCD_S6E8AX0 is not set +# CONFIG_FB_SSD1307 is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_ILI922X is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +CONFIG_LCD_PLATFORM=y +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_LD9040 is not set +# CONFIG_LCD_AMS369FG06 is not set +# CONFIG_LCD_LMS501KF03 is not set +# CONFIG_LCD_HX8357 is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +CONFIG_BACKLIGHT_PWM=y +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LM3630A is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_BACKLIGHT_GPIO is not set +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEOMODE_HELPERS=y +CONFIG_HDMI=y + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_SOUND is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +# CONFIG_HID_ACRUX is not set +CONFIG_HID_APPLE=y +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_AUREAL is not set +CONFIG_HID_BELKIN=y +# CONFIG_HID_BETOP_FF is not set +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +# CONFIG_HID_CP2112 is not set +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +# CONFIG_HID_EMS_FF is not set +# CONFIG_HID_ELECOM is not set +# CONFIG_HID_ELO is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_HOLTEK is not set +# CONFIG_HID_GT683R is not set +# CONFIG_HID_KEYTOUCH is not set +# CONFIG_HID_KYE is not set +# CONFIG_HID_UCLOGIC is not set +# CONFIG_HID_WALTOP is not set +# CONFIG_HID_GYRATION is not set +# CONFIG_HID_ICADE is not set +# CONFIG_HID_TWINHAN is not set +CONFIG_HID_KENSINGTON=y +# CONFIG_HID_LCPOWER is not set +# CONFIG_HID_LENOVO is not set +CONFIG_HID_LOGITECH=y +# CONFIG_HID_LOGITECH_HIDPP is not set +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_LOGIWHEELS_FF is not set +# CONFIG_HID_MAGICMOUSE is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_MULTITOUCH is not set +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +# CONFIG_HID_PANTHERLORD is not set +# CONFIG_HID_PENMOUNT is not set +# CONFIG_HID_PETALYNX is not set +# CONFIG_HID_PICOLCD is not set +# CONFIG_HID_PLANTRONICS is not set +# CONFIG_HID_PRIMAX is not set +# CONFIG_HID_ROCCAT is not set +# CONFIG_HID_SAITEK is not set +# CONFIG_HID_SAMSUNG is not set +# CONFIG_HID_SONY is not set +# CONFIG_HID_SPEEDLINK is not set +# CONFIG_HID_STEELSERIES is not set +# CONFIG_HID_SUNPLUS is not set +# CONFIG_HID_RMI is not set +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TIVO is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THINGM is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_WIIMOTE is not set +# CONFIG_HID_XINMO is not set +# CONFIG_HID_ZEROPLUS is not set +# CONFIG_HID_ZYDACRON is not set +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_OTG_FSM is not set +# CONFIG_USB_ULPI_BUS is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_EXYNOS=y +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_FUSBH200_HCD is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_MAX3421_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +# CONFIG_USB_OHCI_HCD_PLATFORM is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HCD_TEST_MODE is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +# CONFIG_USB_UAS is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USBIP_CORE is not set +# CONFIG_USB_MUSB_HDRC is not set +CONFIG_USB_DWC3=y +CONFIG_USB_DWC3_HOST=y + +# +# Platform Glue Driver Support +# +CONFIG_USB_DWC3_EXYNOS=y + +# +# Debugging features +# +CONFIG_USB_DWC3_DEBUG=y +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_CHIPIDEA is not set +# CONFIG_USB_ISP1760 is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +# CONFIG_USB_EZUSB_FX2 is not set +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +# CONFIG_USB_CHAOSKEY is not set + +# +# USB Physical Layer drivers +# +# CONFIG_USB_PHY is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ULPI is not set +CONFIG_USB_GADGET=m +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_FOTG210_UDC is not set +# CONFIG_USB_GR_UDC is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_BDC_UDC is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_GADGET_XILINX is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_LIBCOMPOSITE=m +CONFIG_USB_F_SS_LB=m +CONFIG_USB_U_ETHER=m +CONFIG_USB_F_ECM=m +CONFIG_USB_F_SUBSET=m +CONFIG_USB_F_RNDIS=m +# CONFIG_USB_CONFIGFS is not set +CONFIG_USB_ZERO=m +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_LED_TRIG is not set +# CONFIG_UWB is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_CLKGATE=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_ARMMMCI is not set +CONFIG_MMC_SDHCI=y +# CONFIG_MMC_SDHCI_PLTFM is not set +CONFIG_MMC_SDHCI_S3C=y +CONFIG_MMC_SDHCI_S3C_DMA=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_IDMAC=y +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_EXYNOS=y +# CONFIG_MMC_DW_K3 is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_USDHI6ROL0 is not set +# CONFIG_MMC_MTK is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +# CONFIG_LEDS_CLASS_FLASH is not set + +# +# LED drivers +# +# CONFIG_LEDS_BCM6328 is not set +# CONFIG_LEDS_BCM6358 is not set +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_LP8860 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_DAC124S085 is not set +CONFIG_LEDS_PWM=m +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_TLC591XX is not set +# CONFIG_LEDS_LM355x is not set + +# +# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) +# +# CONFIG_LEDS_BLINKM is not set +# CONFIG_LEDS_SYSCON is not set +# CONFIG_LEDS_PM8941_WLED is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +CONFIG_LEDS_TRIGGER_BACKLIGHT=y +# CONFIG_LEDS_TRIGGER_CPU is not set +CONFIG_LEDS_TRIGGER_GPIO=y +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_LEDS_TRIGGER_TRANSIENT=y +# CONFIG_LEDS_TRIGGER_CAMERA is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_ABB5ZES3 is not set +# CONFIG_RTC_DRV_ABX80X is not set +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set +CONFIG_RTC_DRV_S5M=y + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_MCP795 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1685_FAMILY is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_HAVE_S3C_RTC=y +CONFIG_RTC_DRV_S3C=y +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_SNVS is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +# CONFIG_AMBA_PL08X is not set +# CONFIG_DW_DMAC is not set +CONFIG_PL330_DMA=y +# CONFIG_FSL_EDMA is not set +# CONFIG_NBPFAXI_DMA is not set +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y + +# +# DMA Clients +# +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_PRISM2_USB is not set +# CONFIG_COMEDI is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_R8188EU is not set +# CONFIG_VT6656 is not set + +# +# IIO staging drivers +# + +# +# Accelerometers +# +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set + +# +# Analog to digital converters +# +# CONFIG_AD7606 is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +# Direct Digital Synthesis +# +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16060 is not set + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_HMC5843_I2C is not set +# CONFIG_SENSORS_HMC5843_SPI is not set + +# +# Active energy metering IC +# +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + +# +# Triggers - standalone +# +# CONFIG_IIO_SIMPLE_DUMMY is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +# CONFIG_STAGING_BOARD is not set +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_LTE_GDM724X is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_DGAP is not set +# CONFIG_GS_FPGABOOT is not set +# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9163=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_FB_FLEX=m +CONFIG_FB_TFT_FBTFT_DEVICE=m +CONFIG_CHROME_PLATFORMS=y +CONFIG_CROS_EC_CHARDEV=y +CONFIG_CROS_EC_PROTO=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_CDCE925 is not set +CONFIG_COMMON_CLK_S2MPS11=y +# CONFIG_CLK_QORIQ is not set +# CONFIG_COMMON_CLK_PWM is not set +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_CDCE706 is not set +CONFIG_COMMON_CLK_SAMSUNG=y + +# +# Hardware Spinlock drivers +# + +# +# Clock Source drivers +# +CONFIG_CLKSRC_OF=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +# CONFIG_ARM_TIMER_SP804 is not set +# CONFIG_ATMEL_PIT is not set +CONFIG_CLKSRC_EXYNOS_MCT=y +CONFIG_CLKSRC_SAMSUNG_PWM=y +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_EM_TIMER_STI is not set +# CONFIG_MAILBOX is not set +CONFIG_IOMMU_SUPPORT=y + +# +# Generic IOMMU Pagetable Support +# +# CONFIG_IOMMU_IO_PGTABLE_LPAE is not set +# CONFIG_EXYNOS_IOMMU is not set +# CONFIG_ARM_SMMU is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# + +# +# SOC (System On Chip) specific Drivers +# +# CONFIG_SUNXI_SRAM is not set +# CONFIG_SOC_TI is not set +CONFIG_PM_DEVFREQ=y + +# +# DEVFREQ Governors +# +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=m +CONFIG_DEVFREQ_GOV_POWERSAVE=m +CONFIG_DEVFREQ_GOV_USERSPACE=m + +# +# DEVFREQ Drivers +# +# CONFIG_ARM_EXYNOS5_BUS_DEVFREQ is not set +CONFIG_PM_DEVFREQ_EVENT=y +CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU=y +CONFIG_EXTCON=y + +# +# Extcon Device Drivers +# +# CONFIG_EXTCON_ADC_JACK is not set +# CONFIG_EXTCON_GPIO is not set +# CONFIG_EXTCON_RT8973A is not set +# CONFIG_EXTCON_SM5502 is not set +# CONFIG_EXTCON_USB_GPIO is not set +# CONFIG_MEMORY is not set +CONFIG_IIO=y +# CONFIG_IIO_BUFFER is not set +# CONFIG_IIO_TRIGGER is not set + +# +# Accelerometers +# +# CONFIG_BMA180 is not set +# CONFIG_BMC150_ACCEL is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set +# CONFIG_KXSD9 is not set +# CONFIG_MMA8452 is not set +# CONFIG_KXCJK1013 is not set +# CONFIG_MMA9551 is not set +# CONFIG_MMA9553 is not set +# CONFIG_STK8312 is not set +# CONFIG_STK8BA50 is not set + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +# CONFIG_AD7291 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7887 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD799X is not set +# CONFIG_CC10001_ADC is not set +CONFIG_EXYNOS_ADC=y +# CONFIG_MAX1027 is not set +# CONFIG_MAX1363 is not set +# CONFIG_MCP320X is not set +# CONFIG_MCP3422 is not set +# CONFIG_NAU7802 is not set +# CONFIG_TI_ADC081C is not set +# CONFIG_TI_ADC128S052 is not set +# CONFIG_VF610_ADC is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# Hid Sensor IIO Common +# + +# +# SSP Sensor Common +# +# CONFIG_IIO_SSP_SENSORHUB is not set + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5686 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD7303 is not set +# CONFIG_M62332 is not set +# CONFIG_MAX517 is not set +# CONFIG_MAX5821 is not set +# CONFIG_MCP4725 is not set +# CONFIG_MCP4922 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADIS16260 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_BMG160 is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set + +# +# Humidity sensors +# +# CONFIG_DHT11 is not set +# CONFIG_SI7005 is not set +# CONFIG_SI7020 is not set + +# +# Inertial measurement units +# +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_KMX61 is not set +# CONFIG_INV_MPU6050_IIO is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_AL3320A is not set +# CONFIG_APDS9300 is not set +# CONFIG_BH1750 is not set +# CONFIG_CM32181 is not set +# CONFIG_CM3232 is not set +# CONFIG_CM3323 is not set +# CONFIG_CM36651 is not set +# CONFIG_GP2AP020A00F is not set +# CONFIG_ISL29125 is not set +# CONFIG_JSA1212 is not set +# CONFIG_LTR501 is not set +# CONFIG_STK3310 is not set +# CONFIG_TCS3414 is not set +# CONFIG_TCS3472 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_TSL4531 is not set +# CONFIG_VCNL4000 is not set + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_AK09911 is not set +# CONFIG_MAG3110 is not set +# CONFIG_MMC35240 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +# CONFIG_BMC150_MAGN is not set + +# +# Inclinometer sensors +# + +# +# Pressure sensors +# +# CONFIG_BMP280 is not set +# CONFIG_MPL115 is not set +# CONFIG_MPL3115 is not set +# CONFIG_MS5611 is not set +# CONFIG_IIO_ST_PRESS is not set +# CONFIG_T5403 is not set + +# +# Lightning sensors +# +# CONFIG_AS3935 is not set + +# +# Proximity sensors +# +# CONFIG_SX9500 is not set + +# +# Temperature sensors +# +# CONFIG_MLX90614 is not set +# CONFIG_TMP006 is not set +CONFIG_PWM=y +CONFIG_PWM_SYSFS=y +# CONFIG_PWM_FSL_FTM is not set +# CONFIG_PWM_PCA9685 is not set +CONFIG_PWM_SAMSUNG=y +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +CONFIG_GIC_NON_BANKED=y +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_FMC is not set + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +CONFIG_PHY_EXYNOS_MIPI_VIDEO=y +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +CONFIG_PHY_EXYNOS_DP_VIDEO=y +# CONFIG_BCM_KONA_USB2_PHY is not set +CONFIG_PHY_EXYNOS5250_SATA=y +CONFIG_PHY_SAMSUNG_USB2=y +CONFIG_PHY_EXYNOS4210_USB2=y +CONFIG_PHY_EXYNOS4X12_USB2=y +CONFIG_PHY_EXYNOS5250_USB2=y +CONFIG_PHY_EXYNOS5_USBDRD=y +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set + +# +# Android +# +# CONFIG_ANDROID is not set + +# +# Firmware Drivers +# +# CONFIG_FIRMWARE_MEMMAP is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=m +CONFIG_EXT4_FS_ENCRYPTION=y +CONFIG_EXT4_DEBUG=y +CONFIG_JBD=y +CONFIG_JBD_DEBUG=y +CONFIG_JBD2=y +CONFIG_JBD2_DEBUG=y +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +CONFIG_REISERFS_CHECK=y +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +CONFIG_JFS_DEBUG=y +CONFIG_JFS_STATISTICS=y +CONFIG_XFS_FS=m +CONFIG_XFS_QUOTA=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_XFS_RT=y +CONFIG_XFS_DEBUG=y +CONFIG_GFS2_FS=m +CONFIG_OCFS2_FS=m +CONFIG_OCFS2_FS_O2CB=m +CONFIG_OCFS2_FS_STATS=y +CONFIG_OCFS2_DEBUG_MASKLOG=y +CONFIG_OCFS2_DEBUG_FS=y +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_BTRFS_FS_CHECK_INTEGRITY=y +CONFIG_BTRFS_FS_RUN_SANITY_TESTS=y +CONFIG_BTRFS_DEBUG=y +CONFIG_BTRFS_ASSERT=y +CONFIG_NILFS2_FS=m +CONFIG_F2FS_FS=m +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +CONFIG_F2FS_FS_SECURITY=y +CONFIG_F2FS_CHECK_FS=y +CONFIG_F2FS_FS_ENCRYPTION=y +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_PRINT_QUOTA_WARNING=y +CONFIG_QUOTA_DEBUG=y +CONFIG_QUOTA_TREE=m +CONFIG_QFMT_V1=m +CONFIG_QFMT_V2=m +CONFIG_QUOTACTL=y +CONFIG_AUTOFS4_FS=m +CONFIG_FUSE_FS=y +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m + +# +# Caches +# +CONFIG_FSCACHE=m +CONFIG_FSCACHE_STATS=y +CONFIG_FSCACHE_HISTOGRAM=y +# CONFIG_FSCACHE_DEBUG is not set +# CONFIG_FSCACHE_OBJECT_LIST is not set +CONFIG_CACHEFILES=m +# CONFIG_CACHEFILES_DEBUG is not set +# CONFIG_CACHEFILES_HISTOGRAM is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=y +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +# CONFIG_PROC_CHILDREN is not set +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +CONFIG_ROMFS_FS=m +CONFIG_ROMFS_BACKED_BY_BLOCK=y +CONFIG_ROMFS_ON_BLOCK=y +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V2=m +CONFIG_NFS_V3=m +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=m +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_PNFS_FILE_LAYOUT=m +CONFIG_PNFS_BLOCK=m +CONFIG_PNFS_FLEXFILE_LAYOUT=m +CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" +CONFIG_NFS_V4_1_MIGRATION=y +CONFIG_NFS_V4_SECURITY_LABEL=y +CONFIG_NFS_FSCACHE=y +CONFIG_NFS_USE_LEGACY_DNS=y +CONFIG_NFS_DEBUG=y +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_PNFS=y +# CONFIG_NFSD_V4_SECURITY_LABEL is not set +CONFIG_NFSD_FAULT_INJECTION=y +CONFIG_GRACE_PERIOD=m +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_SUNRPC_BACKCHANNEL=y +CONFIG_SUNRPC_SWAP=y +CONFIG_SUNRPC_DEBUG=y +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +CONFIG_PRINTK_TIME=y +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_DYNAMIC_DEBUG is not set + +# +# Compile-time checks and compiler options +# +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_INFO_REDUCED is not set +# CONFIG_DEBUG_INFO_SPLIT is not set +# CONFIG_DEBUG_INFO_DWARF4 is not set +# CONFIG_GDB_SCRIPTS is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_PAGE_OWNER is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1 +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_VM is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +CONFIG_LOCKUP_DETECTOR=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHED_INFO=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_DEBUG_TIMEKEEPING is not set +# CONFIG_TIMER_STATS is not set +CONFIG_DEBUG_PREEMPT=y + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_CPU_STALL_INFO=y +# CONFIG_RCU_TRACE is not set +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_FTRACE_SYSCALLS is not set +# CONFIG_TRACER_SNAPSHOT is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_UPROBE_EVENT is not set +# CONFIG_PROBE_EVENTS is not set +# CONFIG_TRACEPOINT_BENCHMARK is not set + +# +# Runtime Testing +# +# CONFIG_LKDTM is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_TEST_HEXDUMP is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_MEMTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_ARM_PTDUMP is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +# CONFIG_DEBUG_UART_8250 is not set +# CONFIG_DEBUG_UART_BCM63XX is not set +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_DEBUG_SET_MODULE_RONX is not set +# CONFIG_CORESIGHT is not set + +# +# Security options +# +CONFIG_KEYS=y +CONFIG_PERSISTENT_KEYRINGS=y +CONFIG_BIG_KEYS=y +CONFIG_TRUSTED_KEYS=m +CONFIG_ENCRYPTED_KEYS=y +CONFIG_SECURITY_DMESG_RESTRICT=y +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_NETWORK_XFRM=y +CONFIG_SECURITY_PATH=y +# CONFIG_SECURITY_SELINUX is not set +CONFIG_SECURITY_SMACK=y +CONFIG_SECURITY_SMACK_BRINGUP=y +CONFIG_SECURITY_TOMOYO=y +CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY=2048 +CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG=1024 +CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER=y +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 +CONFIG_SECURITY_APPARMOR_HASH=y +CONFIG_SECURITY_YAMA=y +CONFIG_SECURITY_YAMA_STACKED=y +CONFIG_INTEGRITY=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +CONFIG_INTEGRITY_AUDIT=y +CONFIG_IMA=y +CONFIG_IMA_MEASURE_PCR_IDX=10 +CONFIG_IMA_LSM_RULES=y +# CONFIG_IMA_TEMPLATE is not set +CONFIG_IMA_NG_TEMPLATE=y +# CONFIG_IMA_SIG_TEMPLATE is not set +CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng" +CONFIG_IMA_DEFAULT_HASH_SHA1=y +# CONFIG_IMA_DEFAULT_HASH_SHA256 is not set +CONFIG_IMA_DEFAULT_HASH="sha1" +CONFIG_IMA_APPRAISE=y +CONFIG_EVM=y +CONFIG_EVM_ATTR_FSUUID=y +CONFIG_EVM_EXTRA_SMACK_XATTRS=y +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set +# CONFIG_DEFAULT_SECURITY_YAMA is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_XOR_BLOCKS=m +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=m +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=m +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_AKCIPHER2=y +# CONFIG_CRYPTO_RSA is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_GF128MUL=m +CONFIG_CRYPTO_NULL=m +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_MCRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +CONFIG_CRYPTO_SEQIV=m +CONFIG_CRYPTO_ECHAINIV=m + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=m +CONFIG_CRYPTO_CTS=m +CONFIG_CRYPTO_ECB=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +CONFIG_CRYPTO_XTS=m + +# +# Hash modes +# +CONFIG_CRYPTO_CMAC=m +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +CONFIG_CRYPTO_GHASH=m +# CONFIG_CRYPTO_POLY1305 is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_842 is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_DRBG_MENU=m +CONFIG_CRYPTO_DRBG_HMAC=y +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +CONFIG_CRYPTO_DRBG=m +CONFIG_CRYPTO_JITTERENTROPY=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +# CONFIG_CRYPTO_USER_API_RNG is not set +# CONFIG_CRYPTO_USER_API_AEAD is not set +CONFIG_CRYPTO_HASH_INFO=y +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_DEV_S5P=y +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_PUBLIC_KEY_ALGO_RSA=y +CONFIG_X509_CERTIFICATE_PARSER=y +# CONFIG_PKCS7_MESSAGE_PARSER is not set +CONFIG_ARM_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM=y +CONFIG_CRYPTO_SHA256_ARM=y +# CONFIG_CRYPTO_SHA512_ARM is not set +CONFIG_CRYPTO_AES_ARM=m +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_RAID6_PQ=m +CONFIG_BITREVERSE=y +CONFIG_HAVE_ARCH_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_PERCPU_RWSEM=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=m +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +CONFIG_CLZ_TAB=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_MPILIB=y +CONFIG_SIGNATURE=y +CONFIG_LIBFDT=y +CONFIG_OID_REGISTRY=y +CONFIG_FONT_SUPPORT=y +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +# CONFIG_FONT_8x16 is not set +# CONFIG_FONT_6x11 is not set +CONFIG_FONT_7x14=y +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_6x10 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +CONFIG_ARCH_HAS_SG_CHAIN=y +# CONFIG_VIRTUALIZATION is not set diff --git a/config/linux-odroid.config b/config/linux-odroid.config new file mode 100644 index 000000000..60c1e576c --- /dev/null +++ b/config/linux-odroid.config @@ -0,0 +1,4546 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.10.92 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_NEED_SG_DMA_LENGTH=y +CONFIG_ARM_DMA_USE_IOMMU=y +CONFIG_ARM_DMA_IOMMU_ALIGNMENT=8 +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_NO_IOPORT=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_NEED_MACH_GPIO_H=y +CONFIG_NEED_MACH_MEMORY_H=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_FHANDLE=y +# CONFIG_AUDIT is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_SPARSE_IRQ=y +CONFIG_KTIME_SCALAR=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_USER_QS is not set +CONFIG_RCU_FANOUT=32 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +# CONFIG_RCU_NOCB_CPU is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +# CONFIG_MEMCG is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_UIDGID_CONVERTED=y +CONFIG_UIDGID_STRICT_TYPE_CHECKS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_HOTPLUG=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +# CONFIG_SLUB_DEBUG is not set +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_BSGLIB=y +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +CONFIG_EFI_PARTITION=y +# CONFIG_SYSV68_PARTITION is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_PADATA=y +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +CONFIG_ARCH_EXYNOS=y +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +CONFIG_PLAT_SAMSUNG=y +CONFIG_PLAT_S5P=y + +# +# Boot options +# +# CONFIG_S3C_BOOT_ERROR_RESET is not set +CONFIG_S3C_BOOT_UART_FORCE_FIFO=y +CONFIG_S3C_LOWLEVEL_UART_PORT=2 +# CONFIG_S5P_CLOCK is not set +CONFIG_SAMSUNG_IRQ_VIC_TIMER=y +# CONFIG_S5P_IRQ is not set +CONFIG_SAMSUNG_GPIOLIB_4BIT=y +CONFIG_S5P_GPIO_DRVSTR=y +CONFIG_SAMSUNG_GPIO_EXTRA=0 +CONFIG_S3C_GPIO_SPACE=0 +CONFIG_S3C_GPIO_TRACK=y +# CONFIG_S3C_ADC is not set +CONFIG_S5P_DEV_MFC=y +CONFIG_S3C24XX_PWM=y +CONFIG_SAMSUNG_DMADEV=y + +# +# Power management +# +# CONFIG_SAMSUNG_PM_DEBUG is not set +# CONFIG_SAMSUNG_PM_CHECK is not set +CONFIG_S5P_PM=y +CONFIG_S5P_SLEEP=y +# CONFIG_SAMSUNG_CORE_TEST is not set +# CONFIG_PLAT_SPEAR is not set + +# +# SAMSUNG EXYNOS SoCs Support +# +# CONFIG_ARCH_EXYNOS4 is not set +CONFIG_ARCH_EXYNOS5=y + +# +# EXYNOS SoCs +# +# CONFIG_SOC_EXYNOS5250 is not set +CONFIG_SOC_EXYNOS5422=y +CONFIG_SOC_EXYNOS5422_REV_0=y +# CONFIG_SOC_EXYNOS5430 is not set +CONFIG_EXYNOS5422_BTS=y +# CONFIG_SOC_EXYNOS5440 is not set +CONFIG_EXYNOS_SNAPSHOT=y +# CONFIG_EXYNOS_SNAPSHOT_REG is not set +# CONFIG_EXYNOS_SNAPSHOT_HRTIMER is not set +# CONFIG_EXYNOS_SNAPSHOT_SOFTIRQ is not set +# CONFIG_EXYNOS_SNAPSHOT_HOOK_LOGGER is not set +CONFIG_EXYNOS_SNAPSHOT_PANIC_REBOOT=y +# CONFIG_EXYNOS_SNAPSHOT_FORCE_DUMP_MODE is not set +CONFIG_EXYNOS5_DYNAMIC_CPU_HOTPLUG=y +CONFIG_EXYNOS_CONTENT_PATH_PROTECTION=y +# CONFIG_ASV_MARGIN_TEST is not set + +# +# Flattened Device Tree based board for EXYNOS SoCs +# +# CONFIG_MACH_EXYNOS5_DT is not set +# CONFIG_MACH_SMDK5422 is not set +# CONFIG_MACH_XYREF5422 is not set +CONFIG_MACH_ODROIDXU3=y +# CONFIG_MACH_XYREF5430 is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_TRUSTZONE=y +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_VIRT_EXT=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_L2_AUTO_CLOCK_DISABLE=y +# CONFIG_CACHE_L2X0 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=16 +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_761320 is not set +# CONFIG_ARM_ERRATA_766421 is not set +# CONFIG_ARM_ERRATA_773022 is not set +# CONFIG_ARM_ERRATA_774769 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +CONFIG_ARM_AMBA=y +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +CONFIG_SCHED_MC=y +# CONFIG_SCHED_SMT is not set +CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE=y +CONFIG_SCHED_HMP=y +# CONFIG_SCHED_HMP_PRIO_FILTER is not set +CONFIG_HMP_FAST_CPU_MASK="4-7" +CONFIG_HMP_SLOW_CPU_MASK="0-3" +CONFIG_HMP_VARIABLE_SCALE=y +CONFIG_HMP_FREQUENCY_INVARIANT_SCALE=y +CONFIG_HAVE_ARM_SCU=y +# CONFIG_HAVE_ARM_ARCH_TIMER is not set +# CONFIG_MCPM is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=8 +CONFIG_HOTPLUG_CPU=y +# CONFIG_ARM_PSCI is not set +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=200 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_MEMORY_ISOLATION=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_CLEANCACHE is not set +# CONFIG_FRONTSWAP is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +CONFIG_SECCOMP=y +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_XEN is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +CONFIG_USE_OF=y +# CONFIG_ATAGS is not set +CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y +CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES="exynos5422-odroidxu3" +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER is not set +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND=y +CONFIG_CMDLINE="console=ttySAC2,115200n8 vmalloc=512M" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_GENERIC_CPUFREQ_CPU0 is not set + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set +# CONFIG_ARM_EXYNOS_CPUFREQ is not set +CONFIG_ARM_EXYNOS_MP_CPUFREQ=y +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5430_CPUFREQ is not set +CONFIG_ARM_EXYNOS5422_CPUFREQ=y +# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +CONFIG_CPU_IDLE=y +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set +# CONFIG_EXYNOS_CPUIDLE_C2 is not set +# CONFIG_EXYNOS_IDLE_CLOCK_DOWN is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +CONFIG_BINFMT_MISC=m +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=100 +CONFIG_PM_WAKELOCKS_GC=y +CONFIG_PM_RUNTIME=y +CONFIG_PM_RUNTIME_TEST_SYSFS=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +CONFIG_PM_ADVANCED_DEBUG=y +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_HAS_OPP=y +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +CONFIG_PM_GENERIC_DOMAINS=y +CONFIG_PM_GENERIC_DOMAINS_SLEEP=y +CONFIG_PM_GENERIC_DOMAINS_RUNTIME=y +CONFIG_CPU_PM=y +# CONFIG_SUSPEND_TIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_MIGRATE=y +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_ROUTE_CLASSID=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IP_TUNNEL=y +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_ARPD=y +CONFIG_SYN_COOKIES=y +# CONFIG_NET_IPVTI is not set +CONFIG_INET_AH=m +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_TUNNEL=m +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +CONFIG_INET_LRO=m +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=m +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=m +CONFIG_TCP_CONG_HTCP=m +CONFIG_TCP_CONG_HSTCP=m +# CONFIG_TCP_CONG_HYBLA is not set +# CONFIG_TCP_CONG_VEGAS is not set +# CONFIG_TCP_CONG_SCALABLE is not set +# CONFIG_TCP_CONG_LP is not set +# CONFIG_TCP_CONG_VENO is not set +# CONFIG_TCP_CONG_YEAH is not set +# CONFIG_TCP_CONG_ILLINOIS is not set +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +# CONFIG_IPV6_ROUTE_INFO is not set +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_ACCT=m +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_ZONES is not set +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_LABELS=y +CONFIG_NF_CT_PROTO_DCCP=m +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_SCTP=m +CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_BROADCAST=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_CT_NETLINK_TIMEOUT=m +CONFIG_NF_CT_NETLINK_HELPER=m +CONFIG_NETFILTER_NETLINK_QUEUE_CT=y +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PROTO_DCCP=m +CONFIG_NF_NAT_PROTO_UDPLITE=m +CONFIG_NF_NAT_PROTO_SCTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_SIP=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NETFILTER_TPROXY=m +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=m +CONFIG_NETFILTER_XT_CONNMARK=m +# CONFIG_NETFILTER_XT_SET is not set + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NETMAP=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_REDIRECT=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m + +# +# Xtables matches +# +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ECN=y +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +# CONFIG_NETFILTER_XT_MATCH_IPVS is not set +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_SET_MAX=256 +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_IP_VS=m +# CONFIG_IP_VS_IPV6 is not set +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_AH_ESP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS SH scheduler +# +CONFIG_IP_VS_SH_TAB_BITS=8 + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IP_VS_NFCT=y +CONFIG_IP_VS_PE_SIP=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_NF_NAT_IPV6=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_ULOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_STP=m +CONFIG_GARP=m +CONFIG_MRP=m +CONFIG_BRIDGE=m +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_HAVE_NET_DSA=y +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +# CONFIG_DECNET is not set +CONFIG_LLC=m +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +CONFIG_IPDDP=m +# CONFIG_IPDDP_ENCAP is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +CONFIG_PHONET=y +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_SCH_PLUG=m + +# +# Classification +# +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_IPSET=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_IPT=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_SIMP=m +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +CONFIG_DNS_RESOLVER=m +# CONFIG_BATMAN_ADV is not set +CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_NETLINK_MMAP=y +CONFIG_NETLINK_DIAG=m +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +CONFIG_NETPRIO_CGROUP=m +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_LL=y +CONFIG_BT_HCIUART_3WIRE=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIVHCI=m +# CONFIG_BT_MRVL is not set +# CONFIG_BT_ATH3K is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +CONFIG_WIMAX=m +CONFIG_WIMAX_DEBUG_LEVEL=8 +CONFIG_RFKILL=m +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +CONFIG_RFKILL_REGULATOR=m +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +CONFIG_NFC=m +CONFIG_NFC_NCI=m +CONFIG_NFC_HCI=m +# CONFIG_NFC_SHDLC is not set + +# +# Near Field Communication (NFC) devices +# +CONFIG_NFC_PN533=m +# CONFIG_NFC_PN544 is not set +# CONFIG_NFC_MICROREAD is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="mfc_fw.bin edid/1024x768.bin edid/1280x800.bin edid/1920x1080.bin edid/720x480.bin edid/800x600.bin edid/800x480.bin edid/1280x1024.bin edid/1280x720.bin edid/1440x900.bin edid/1680x1050.bin edid/1920x1080_50hz.bin edid/640x480.bin edid/720x576.bin" +CONFIG_EXTRA_FIRMWARE_DIR="firmware" +# CONFIG_FW_LOADER_USER_HELPER is not set +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_SPI=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set + +# +# Default contiguous memory area size: +# +CONFIG_CMA_SIZE_MBYTES=256 +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_ALIGNMENT=8 +CONFIG_CMA_AREAS=16 + +# +# Bus devices +# +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +CONFIG_PROC_DEVICETREE=y +CONFIG_OF_SELFTEST=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_DEVICE=y +CONFIG_OF_I2C=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +CONFIG_PARPORT=m +# CONFIG_PARPORT_PC is not set +# CONFIG_PARPORT_GSC is not set +# CONFIG_PARPORT_AX88796 is not set +# CONFIG_PARPORT_1284 is not set +CONFIG_PARPORT_NOT_PC=y +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +CONFIG_BLK_DEV_CRYPTOLOOP=y +# CONFIG_BLK_DEV_DRBD is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=m +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +CONFIG_ATA_OVER_ETH=m +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set +# CONFIG_MIPI_LLI is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +CONFIG_SCSI_NETLINK=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +CONFIG_SCSI_FC_ATTRS=m +CONFIG_SCSI_ISCSI_ATTRS=m +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +CONFIG_ISCSI_TCP=m +CONFIG_ISCSI_BOOT_SYSFS=m +# CONFIG_SCSI_UFSHCD is not set +CONFIG_LIBFC=m +CONFIG_LIBFCOE=m +CONFIG_FCOE=m +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +# CONFIG_BCACHE is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_BUFIO=m +CONFIG_DM_BIO_PRISON=m +CONFIG_DM_PERSISTENT_DATA=m +CONFIG_DM_CRYPT=y +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_DM_DEBUG_BLOCK_STACK_TRACING=y +CONFIG_DM_CACHE=m +CONFIG_DM_CACHE_MQ=m +CONFIG_DM_CACHE_CLEANER=m +CONFIG_DM_MIRROR=m +CONFIG_DM_RAID=m +# CONFIG_DM_LOG_USERSPACE is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_UEVENT is not set +# CONFIG_DM_FLAKEY is not set +CONFIG_DM_VERITY=m +CONFIG_TARGET_CORE=m +CONFIG_TCM_IBLOCK=m +CONFIG_TCM_FILEIO=m +CONFIG_TCM_PSCSI=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_TCM_FC=m +CONFIG_ISCSI_TARGET=m +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +CONFIG_BONDING=m +CONFIG_DUMMY=m +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_IFB is not set +CONFIG_NET_TEAM=m +CONFIG_NET_TEAM_MODE_BROADCAST=m +CONFIG_NET_TEAM_MODE_ROUNDROBIN=m +CONFIG_NET_TEAM_MODE_RANDOM=m +CONFIG_NET_TEAM_MODE_ACTIVEBACKUP=m +CONFIG_NET_TEAM_MODE_LOADBALANCE=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +# CONFIG_VXLAN is not set +CONFIG_NETCONSOLE=m +CONFIG_NETCONSOLE_DYNAMIC=y +CONFIG_NETPOLL=y +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +CONFIG_TUN=y +CONFIG_VETH=m + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +CONFIG_ETHERNET=y +# CONFIG_NET_CADENCE is not set +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +# CONFIG_NET_VENDOR_I825XX is not set +CONFIG_NET_VENDOR_MARVELL=y +# CONFIG_MVMDIO is not set +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NET_VENDOR_MICROCHIP=y +# CONFIG_ENC28J60 is not set +CONFIG_NET_VENDOR_NATSEMI=y +# CONFIG_NET_VENDOR_8390 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SMSC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NET_VENDOR_WIZNET=y +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=m +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPPOLAC=m +CONFIG_PPPOPNS=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +# CONFIG_SLIP is not set +CONFIG_SLHC=m + +# +# USB Network Adapters +# +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +# CONFIG_USB_NET_ZAURUS is not set +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_CDC_PHONET=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +# CONFIG_WLAN is not set + +# +# WiMAX Wireless Broadband devices +# +CONFIG_WIMAX_I2400M=m +CONFIG_WIMAX_I2400M_USB=m +CONFIG_WIMAX_I2400M_DEBUG_LEVEL=8 +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=y +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set +CONFIG_INPUT_MATRIXKMAP=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +CONFIG_INPUT_JOYDEV=y +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_KEYRESET=y +CONFIG_GLOVE_TOUCH=y + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +CONFIG_KEYBOARD_SAMSUNG=y +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +CONFIG_INPUT_MOUSE=y +# CONFIG_MOUSE_PS2 is not set +# CONFIG_MOUSE_SERIAL is not set +CONFIG_MOUSE_APPLETOUCH=m +CONFIG_MOUSE_BCM5974=m +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +CONFIG_MOUSE_SYNAPTICS_USB=m +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_ANALOG=m +CONFIG_JOYSTICK_A3D=m +CONFIG_JOYSTICK_ADI=m +CONFIG_JOYSTICK_COBRA=m +CONFIG_JOYSTICK_GF2K=m +CONFIG_JOYSTICK_GRIP=m +CONFIG_JOYSTICK_GRIP_MP=m +CONFIG_JOYSTICK_GUILLEMOT=m +CONFIG_JOYSTICK_INTERACT=m +CONFIG_JOYSTICK_SIDEWINDER=m +CONFIG_JOYSTICK_TMDC=m +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y +CONFIG_JOYSTICK_IFORCE_232=y +CONFIG_JOYSTICK_WARRIOR=m +CONFIG_JOYSTICK_MAGELLAN=m +CONFIG_JOYSTICK_SPACEORB=m +CONFIG_JOYSTICK_SPACEBALL=m +CONFIG_JOYSTICK_STINGER=m +CONFIG_JOYSTICK_TWIDJOY=m +CONFIG_JOYSTICK_ZHENHUA=m +CONFIG_JOYSTICK_DB9=m +CONFIG_JOYSTICK_GAMECON=m +CONFIG_JOYSTICK_TURBOGRAFX=m +CONFIG_JOYSTICK_AS5011=m +CONFIG_JOYSTICK_JOYDUMP=m +CONFIG_JOYSTICK_XPAD=m +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_JOYSTICK_WALKERA0701=m +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_WM97XX is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_MXT540E is not set +CONFIG_TOUCHSCREEN_DWAV_USB_MT=m +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=m +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_PWM_BEEPER is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_CMA3000 is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_PARKBD is not set +# CONFIG_SERIO_AMBAKMI is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_APBPS2 is not set +CONFIG_GAMEPORT=m +CONFIG_GAMEPORT_NS558=m +CONFIG_GAMEPORT_L4=m + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +# CONFIG_SERIAL_8250_CONSOLE is not set +CONFIG_SERIAL_8250_DMA=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_DW is not set +# CONFIG_SERIAL_8250_EM is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_AMBA_PL010 is not set +# CONFIG_SERIAL_AMBA_PL011 is not set +CONFIG_SERIAL_SAMSUNG=y +# CONFIG_SERIAL_SAMSUNG_DMA is not set +CONFIG_SERIAL_SAMSUNG_UARTS_4=y +CONFIG_SERIAL_SAMSUNG_UARTS=4 +CONFIG_SERIAL_SAMSUNG_CONSOLE=y +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_TTY_PRINTK is not set +CONFIG_PRINTER=m +# CONFIG_LP_CONSOLE is not set +CONFIG_PPDEV=m +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_HW_RANDOM_ATMEL is not set +CONFIG_HW_RANDOM_EXYNOS=y +CONFIG_HW_RANDOM_EXYNOS_SWD=y +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +CONFIG_I2C_MUX_GPIO=m +CONFIG_I2C_MUX_PCA9541=m +CONFIG_I2C_MUX_PCA954x=m +CONFIG_I2C_MUX_PINCTRL=m +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +CONFIG_I2C_EXYNOS5=y +CONFIG_I2C_GPIO=y +CONFIG_I2C_GPIO_CUSTOM=m +# CONFIG_I2C_NOMADIK is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +CONFIG_HAVE_S3C2410_I2C=y +CONFIG_I2C_S3C2410=y +CONFIG_HAVE_EXYNOS5_HSI2C=y +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_BUTTERFLY is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_LM70_LLP is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PL022 is not set +# CONFIG_SPI_PXA2XX_PCI is not set +CONFIG_SPI_S3C64XX=m +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=m +# CONFIG_SPI_TLE62X0 is not set + +# +# Qualcomm MSM SSBI bus support +# +# CONFIG_SSBI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PINCTRL_SAMSUNG=y +CONFIG_PINCTRL_EXYNOS=y +# CONFIG_PINCTRL_EXYNOS5440 is not set +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIOLIB=y +CONFIG_OF_GPIO=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_PL061 is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_GRGPIO is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# + +# +# USB GPIO expanders: +# +CONFIG_W1=m + +# +# 1-wire Bus Masters +# +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2408 is not set +# CONFIG_W1_SLAVE_DS2413 is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_DS2780 is not set +# CONFIG_W1_SLAVE_DS2781 is not set +# CONFIG_W1_SLAVE_DS28E04 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +CONFIG_CHARGER_BQ24160=y +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GOLDFISH is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_AVS is not set +# CONFIG_HWMON is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_USER_SPACE is not set +CONFIG_CPU_THERMAL=y +# CONFIG_THERMAL_EMULATION is not set +CONFIG_EXYNOS_THERMAL=y +# CONFIG_CPU_THERMAL_IPA is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_ARM_SP805_WATCHDOG is not set +CONFIG_HAVE_S3C2410_WATCHDOG=y +CONFIG_S3C2410_WATCHDOG=m +# CONFIG_DW_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_UCB1400_CORE is not set +# CONFIG_MFD_RC5T583 is not set +CONFIG_MFD_SEC_CORE=y +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_STMPE is not set +CONFIG_MFD_SYSCON=y +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +CONFIG_REGULATOR_FIXED_VOLTAGE=y +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_ANATOP is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +CONFIG_REGULATOR_S2MPS11=y +# CONFIG_REGULATOR_S2MPS13 is not set +# CONFIG_REGULATOR_S5M8767 is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_MEDIA_RC_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_MEDIA_CONTROLLER_DVB=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_V4L2=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEO_TUNER=m +CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_VMALLOC=m +CONFIG_VIDEOBUF_DVB=m +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_VMALLOC=m +CONFIG_VIDEOBUF2_ION=y +# CONFIG_VIDEO_V4L2_INT_DEVICE is not set +CONFIG_DVB_CORE=y +CONFIG_DVB_NET=y +CONFIG_TTPCI_EEPROM=m +CONFIG_DVB_MAX_ADAPTERS=8 +# CONFIG_DVB_DYNAMIC_MINORS is not set + +# +# Media drivers +# +CONFIG_RC_CORE=y +CONFIG_RC_MAP=y +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_RC5_SZ_DECODER=y +CONFIG_IR_SANYO_DECODER=y +CONFIG_IR_MCE_KBD_DECODER=y +CONFIG_RC_DEVICES=y +CONFIG_RC_ATI_REMOTE=m +CONFIG_IR_IMON=m +CONFIG_IR_MCEUSB=m +CONFIG_IR_REDRAT3=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_IGUANA=m +CONFIG_IR_TTUSBIR=m +CONFIG_RC_LOOPBACK=m +CONFIG_IR_GPIO_CIR=m +CONFIG_IR_GPIOPLUG_CIR=m +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +CONFIG_USB_M5602=m +CONFIG_USB_STV06XX=m +CONFIG_USB_GL860=m +CONFIG_USB_GSPCA_BENQ=m +CONFIG_USB_GSPCA_CONEX=m +CONFIG_USB_GSPCA_CPIA1=m +CONFIG_USB_GSPCA_ETOMS=m +CONFIG_USB_GSPCA_FINEPIX=m +CONFIG_USB_GSPCA_JEILINJ=m +CONFIG_USB_GSPCA_JL2005BCD=m +CONFIG_USB_GSPCA_KINECT=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_MARS=m +CONFIG_USB_GSPCA_MR97310A=m +CONFIG_USB_GSPCA_NW80X=m +CONFIG_USB_GSPCA_OV519=m +CONFIG_USB_GSPCA_OV534=m +CONFIG_USB_GSPCA_OV534_9=m +CONFIG_USB_GSPCA_PAC207=m +CONFIG_USB_GSPCA_PAC7302=m +CONFIG_USB_GSPCA_PAC7311=m +CONFIG_USB_GSPCA_SE401=m +CONFIG_USB_GSPCA_SN9C2028=m +CONFIG_USB_GSPCA_SN9C20X=m +CONFIG_USB_GSPCA_SONIXB=m +CONFIG_USB_GSPCA_SONIXJ=m +CONFIG_USB_GSPCA_SPCA500=m +CONFIG_USB_GSPCA_SPCA501=m +CONFIG_USB_GSPCA_SPCA505=m +CONFIG_USB_GSPCA_SPCA506=m +CONFIG_USB_GSPCA_SPCA508=m +CONFIG_USB_GSPCA_SPCA561=m +CONFIG_USB_GSPCA_SPCA1528=m +CONFIG_USB_GSPCA_SQ905=m +CONFIG_USB_GSPCA_SQ905C=m +CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_STK014=m +CONFIG_USB_GSPCA_STV0680=m +CONFIG_USB_GSPCA_SUNPLUS=m +CONFIG_USB_GSPCA_T613=m +CONFIG_USB_GSPCA_TOPRO=m +CONFIG_USB_GSPCA_TV8532=m +CONFIG_USB_GSPCA_VC032X=m +CONFIG_USB_GSPCA_VICAM=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m +CONFIG_USB_GSPCA_ZC3XX=m +CONFIG_USB_PWC=m +CONFIG_USB_PWC_DEBUG=y +CONFIG_USB_PWC_INPUT_EVDEV=y +CONFIG_VIDEO_CPIA2=m +CONFIG_USB_ZR364XX=m +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_USB_SN9C102=m + +# +# Analog TV USB devices +# +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_SYSFS=y +CONFIG_VIDEO_PVRUSB2_DVB=y +# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_TLG2300=m +CONFIG_VIDEO_USBVISION=m +CONFIG_VIDEO_STK1160=m +CONFIG_VIDEO_STK1160_AC97=y + +# +# Analog/digital TV USB devices +# +CONFIG_VIDEO_AU0828=m +CONFIG_VIDEO_AU0828_V4L2=y +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_RC=y +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_VIDEO_TM6000=m +CONFIG_VIDEO_TM6000_ALSA=m +CONFIG_VIDEO_TM6000_DVB=m + +# +# Digital TV USB devices +# +CONFIG_DVB_USB=m +# CONFIG_DVB_USB_DEBUG is not set +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_PCTV452E=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_FRIIO=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_USB_V2=m +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_AF9035=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_AZ6007=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_IT913X=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_USB_MXL111SF=m +CONFIG_DVB_USB_RTL28XXU=m +CONFIG_DVB_USB_DVBSKY=m +CONFIG_SMS_USB_DRV=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +# CONFIG_DVB_B2C2_FLEXCOP_USB_DEBUG is not set + +# +# Webcam, TV (analog/digital) USB devices +# +CONFIG_VIDEO_EM28XX=m +CONFIG_VIDEO_EM28XX_ALSA=m +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_VIDEO_EM28XX_RC=m +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_EXYNOS=y +# CONFIG_VIDEO_EXYNOS_GSCALER_1_3 is not set +# CONFIG_VIDEO_EXYNOS_GSCALER is not set +CONFIG_VIDEO_EXYNOS_MFC=y +# CONFIG_EXYNOS_MFC_V6 is not set +CONFIG_EXYNOS_MFC_V8=y +# CONFIG_VIDEO_EXYNOS_HEVC is not set +# CONFIG_VIDEO_EXYNOS_FIMC_IS is not set +# CONFIG_VIDEO_EXYNOS_SCALER is not set +# CONFIG_VIDEO_EXYNOS_FIMG2D is not set +# CONFIG_VIDEO_EXYNOS_JPEG is not set +# CONFIG_VIDEO_EXYNOS_HX_JPEG is not set +# CONFIG_VIDEO_EXYNOS_TV is not set +CONFIG_VIDEO_EXYNOS_HDMI_CEC=y +# CONFIG_VIDEO_TIMBERDALE is not set +# CONFIG_SOC_CAMERA is not set +# CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS is not set +# CONFIG_VIDEO_SAMSUNG_S5P_TV is not set +CONFIG_V4L_MEM2MEM_DRIVERS=y +# CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set +# CONFIG_VIDEO_SAMSUNG_S5P_G2D is not set +# CONFIG_VIDEO_SAMSUNG_S5P_JPEG is not set +# CONFIG_VIDEO_SAMSUNG_S5P_MFC is not set +# CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC is not set +# CONFIG_VIDEO_SH_VEU is not set +# CONFIG_V4L_TEST_DRIVERS is not set + +# +# Supported MMC/SDIO adapters +# +# CONFIG_SMS_SDIO_DRV is not set +# CONFIG_MEDIA_PARPORT_SUPPORT is not set +CONFIG_RADIO_ADAPTERS=y +# CONFIG_RADIO_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_RADIO_SHARK is not set +# CONFIG_RADIO_SHARK2 is not set +# CONFIG_I2C_SI4713 is not set +# CONFIG_RADIO_SI4713 is not set +# CONFIG_USB_KEENE is not set +# CONFIG_USB_MA901 is not set +# CONFIG_RADIO_TEA5764 is not set +# CONFIG_RADIO_SAA7706H is not set +# CONFIG_RADIO_TEF6862 is not set +# CONFIG_RADIO_WL1273 is not set + +# +# Texas Instruments WL128x FM driver (ST based) +# +# CONFIG_RADIO_WL128X is not set +CONFIG_MEDIA_COMMON_OPTIONS=y + +# +# common driver options +# +CONFIG_VIDEO_CX2341X=m +CONFIG_VIDEO_TVEEPROM=m +CONFIG_CYPRESS_FIRMWARE=m +CONFIG_DVB_B2C2_FLEXCOP=m +CONFIG_SMS_SIANO_MDTV=m +CONFIG_SMS_SIANO_RC=y +# CONFIG_SMS_SIANO_DEBUGFS is not set + +# +# Media ancillary drivers (tuners, sensors, i2c, frontends) +# +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_MEDIA_ATTACH=y +CONFIG_VIDEO_IR_I2C=y + +# +# Audio decoders, processors and mixers +# +CONFIG_VIDEO_MSP3400=m +CONFIG_VIDEO_CS53L32A=m +CONFIG_VIDEO_WM8775=m + +# +# RDS decoders +# + +# +# Video decoders +# +CONFIG_VIDEO_SAA711X=m +CONFIG_VIDEO_TVP5150=m + +# +# Video and audio decoders +# +CONFIG_VIDEO_CX25840=m + +# +# Video encoders +# + +# +# Camera sensor devices +# +CONFIG_VIDEO_MT9V011=m + +# +# Flash devices +# + +# +# Video improvement chips +# + +# +# Miscelaneous helper chips +# + +# +# Sensors used on soc_camera driver +# +CONFIG_MEDIA_TUNER=y +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5761=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_MT2060=m +CONFIG_MEDIA_TUNER_MT2063=m +CONFIG_MEDIA_TUNER_MT2266=m +CONFIG_MEDIA_TUNER_QT1010=m +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MXL5005S=m +CONFIG_MEDIA_TUNER_MXL5007T=m +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_MEDIA_TUNER_MAX2165=m +CONFIG_MEDIA_TUNER_TDA18218=m +CONFIG_MEDIA_TUNER_FC0011=m +CONFIG_MEDIA_TUNER_FC0012=m +CONFIG_MEDIA_TUNER_FC0013=m +CONFIG_MEDIA_TUNER_TDA18212=m +CONFIG_MEDIA_TUNER_E4000=m +CONFIG_MEDIA_TUNER_FC2580=m +CONFIG_MEDIA_TUNER_M88TS2022=m +CONFIG_MEDIA_TUNER_TUA9001=m +CONFIG_MEDIA_TUNER_SI2157=m +CONFIG_MEDIA_TUNER_IT913X=m +CONFIG_MEDIA_TUNER_R820T=m + +# +# Multistandard (satellite) frontends +# +CONFIG_DVB_STB0899=m +CONFIG_DVB_STB6100=m +CONFIG_DVB_STV090x=m +CONFIG_DVB_STV6110x=m +CONFIG_DVB_M88DS3103=m + +# +# Multistandard (cable + terrestrial) frontends +# +CONFIG_DVB_DRXK=m +CONFIG_DVB_TDA18271C2DD=m + +# +# DVB-S (satellite) frontends +# +CONFIG_DVB_CX24123=m +CONFIG_DVB_MT312=m +CONFIG_DVB_ZL10039=m +CONFIG_DVB_S5H1420=m +CONFIG_DVB_STV0288=m +CONFIG_DVB_STB6000=m +CONFIG_DVB_STV0299=m +CONFIG_DVB_STV6110=m +CONFIG_DVB_STV0900=m +CONFIG_DVB_TDA10086=m +CONFIG_DVB_TUNER_ITD1000=m +CONFIG_DVB_TUNER_CX24113=m +CONFIG_DVB_TDA826X=m +CONFIG_DVB_CX24116=m +CONFIG_DVB_SI21XX=m +CONFIG_DVB_TS2020=m +CONFIG_DVB_DS3000=m +CONFIG_DVB_TDA10071=m + +# +# DVB-T (terrestrial) frontends +# +CONFIG_DVB_CX22702=m +CONFIG_DVB_DRXD=m +CONFIG_DVB_TDA1004X=m +CONFIG_DVB_NXT6000=m +CONFIG_DVB_MT352=m +CONFIG_DVB_ZL10353=m +CONFIG_DVB_DIB3000MB=m +CONFIG_DVB_DIB3000MC=m +CONFIG_DVB_DIB7000M=m +CONFIG_DVB_DIB7000P=m +CONFIG_DVB_TDA10048=m +CONFIG_DVB_AF9013=m +CONFIG_DVB_EC100=m +CONFIG_DVB_CXD2820R=m +CONFIG_DVB_RTL2830=m +CONFIG_DVB_RTL2832=m +CONFIG_DVB_SI2168=m +# CONFIG_DVB_AS102_FE is not set + +# +# DVB-C (cable) frontends +# +CONFIG_DVB_TDA10023=m +CONFIG_DVB_STV0297=m + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# +CONFIG_DVB_NXT200X=m +CONFIG_DVB_BCM3510=m +CONFIG_DVB_LGDT330X=m +CONFIG_DVB_LGDT3305=m +CONFIG_DVB_LG2160=m +CONFIG_DVB_S5H1409=m +CONFIG_DVB_AU8522=m +CONFIG_DVB_AU8522_DTV=m +CONFIG_DVB_AU8522_V4L=m +CONFIG_DVB_S5H1411=m + +# +# ISDB-T (terrestrial) frontends +# +CONFIG_DVB_S921=m +CONFIG_DVB_DIB8000=m +CONFIG_DVB_MB86A20S=m + +# +# Digital terrestrial only tuners/PLL +# +CONFIG_DVB_PLL=m +CONFIG_DVB_TUNER_DIB0070=m +CONFIG_DVB_TUNER_DIB0090=m + +# +# SEC control devices for DVB-S +# +CONFIG_DVB_LNBP21=m +CONFIG_DVB_LNBP22=m +CONFIG_DVB_ISL6421=m +CONFIG_DVB_ISL6423=m +CONFIG_DVB_A8293=m +CONFIG_DVB_SP2=m +CONFIG_DVB_LGS8GXX=m +CONFIG_DVB_ATBM8830=m +CONFIG_DVB_IX2505V=m +CONFIG_DVB_IT913X_FE=m +CONFIG_DVB_M88RS2000=m +CONFIG_DVB_AF9033=m + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set + +# +# Graphics support +# +CONFIG_DRM=y +CONFIG_DRM_USB=m +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_KMS_FB_HELPER=y +CONFIG_DRM_LOAD_EDID_FIRMWARE=y + +# +# I2C encoder or helper chips +# +# CONFIG_DRM_I2C_CH7006 is not set +# CONFIG_DRM_I2C_SIL164 is not set +# CONFIG_DRM_I2C_NXP_TDA998X is not set +# CONFIG_DRM_PTN3460 is not set +CONFIG_DRM_EXYNOS=y +CONFIG_DRM_EXYNOS_IOMMU=y +CONFIG_DRM_EXYNOS_DMABUF=y +CONFIG_DRM_EXYNOS_FIMD=y +# CONFIG_DRM_EXYNOS_DPI is not set +# CONFIG_DRM_EXYNOS_DSI is not set +CONFIG_DRM_EXYNOS_DP=y +CONFIG_DRM_EXYNOS_HDMI=y +# CONFIG_DRM_EXYNOS_VIDI is not set +# CONFIG_DRM_EXYNOS_G2D is not set +# CONFIG_DRM_EXYNOS_IPP is not set +CONFIG_DRM_UDL=m +# CONFIG_DRM_ARMADA is not set +# CONFIG_DRM_TILCDC is not set + +# +# ARM GPU Configuration +# +CONFIG_MALI_MIDGARD=y +# CONFIG_MALI_GATOR_SUPPORT is not set +CONFIG_MALI_MIDGARD_DVFS=y +CONFIG_MALI_MIDGARD_RT_PM=y +# CONFIG_MALI_MIDGARD_ENABLE_TRACE is not set +CONFIG_MALI_MIDGARD_DEBUG_SYS=y +CONFIG_MALI_EXPERT=y +# CONFIG_MALI_DEVFREQ is not set +# CONFIG_MALI_DEBUG_SHADER_SPLIT_FS is not set +CONFIG_MALI_PLATFORM_FAKE=y +CONFIG_MALI_PLATFORM_THIRDPARTY=y +CONFIG_MALI_PLATFORM_THIRDPARTY_NAME="5422" +# CONFIG_MALI_DEBUG is not set +# CONFIG_MALI_NO_MALI is not set +# CONFIG_MALI_TRACE_TIMELINE is not set +# CONFIG_MALI_SYSTEM_TRACE is not set +# CONFIG_MALI_GPU_TRACEPOINTS is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_VIDEOMODE_HELPERS=y +CONFIG_HDMI=y +CONFIG_FB=y +CONFIG_FIRMWARE_EDID=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +CONFIG_FB_SYS_FILLRECT=m +CONFIG_FB_SYS_COPYAREA=m +CONFIG_FB_SYS_IMAGEBLIT=m +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=m +CONFIG_FB_DEFERRED_IO=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +CONFIG_FB_BACKLIGHT=y +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_ARMCLCD is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_DECON_TV is not set +# CONFIG_DECON is not set +# CONFIG_FB_S3C is not set +# CONFIG_FB_SMSCUFX is not set +CONFIG_FB_UDL=m +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_ILI922X is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +# CONFIG_LCD_PLATFORM is not set +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_LD9040 is not set +# CONFIG_LCD_AMS369FG06 is not set +# CONFIG_LCD_LMS501KF03 is not set +# CONFIG_LCD_HX8357 is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +# CONFIG_BACKLIGHT_PWM is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LM3630 is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LP855X is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_FB_SSD1307 is not set +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_FB_FLEX=m +CONFIG_FB_TFT_FBTFT_DEVICE=m +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +CONFIG_SND_COMPRESS_OFFLOAD=y +CONFIG_SND_JACK=y +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_SEQUENCER_OSS=y +CONFIG_SND_HRTIMER=y +CONFIG_SND_SEQ_HRTIMER_DEFAULT=y +CONFIG_SND_DYNAMIC_MINORS=y +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_VMASTER=y +CONFIG_SND_RAWMIDI_SEQ=m +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +CONFIG_SND_ALOOP=m +CONFIG_SND_VIRMIDI=m +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_MTS64 is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_PORTMAN2X4 is not set +# CONFIG_SND_AC97_POWER_SAVE is not set +CONFIG_SND_ARM=y +# CONFIG_SND_ARMAACI is not set +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_6FIRE is not set +CONFIG_SND_SOC=y +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_SOC_SAMSUNG=y +CONFIG_SND_SAMSUNG_I2S=y +CONFIG_SND_SAMSUNG_AUDSS=y +# CONFIG_SND_SAMSUNG_IOMMU is not set +# CONFIG_SND_SAMSUNG_FAKEDMA is not set +# CONFIG_SND_SOC_SAMSUNG_SMDK_WM8994 is not set +# CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF is not set +# CONFIG_SND_SOC_SMDK_WM8994_PCM is not set +# CONFIG_SND_SOC_SAMSUNG_XYREF5430_ES515 is not set +# CONFIG_SND_SOC_SAMSUNG_XYREF5422_ES515 is not set +CONFIG_SND_SOC_SAMSUNG_ODROID_MAX98090=y +# CONFIG_SND_SOC_SAMSUNG_SMDK5422_WM8994 is not set +# CONFIG_SND_SAMSUNG_SEIREN is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_MAX98090=y +CONFIG_SND_SOC_DUMMY_CODEC=y +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=m + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +CONFIG_HIDRAW=y +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=m +CONFIG_HID_APPLEIR=m +CONFIG_HID_AUREAL=m +CONFIG_HID_BELKIN=m +CONFIG_HID_CHERRY=m +CONFIG_HID_CHICONY=m +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_CYPRESS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=m +CONFIG_HID_ELECOM=m +CONFIG_HID_EZKEY=m +CONFIG_HID_HOLTEK=m +# CONFIG_HOLTEK_FF is not set +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_TWINHAN=m +CONFIG_HID_KENSINGTON=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LENOVO_TPKBD=m +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=m +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTRIG=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_PICOLCD_FB=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LCD=y +CONFIG_HID_PICOLCD_LEDS=y +CONFIG_HID_PICOLCD_CIR=y +CONFIG_HID_PRIMAX=m +CONFIG_HID_PS3REMOTE=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_SAITEK=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_HID_SPEEDLINK=m +CONFIG_HID_STEELSERIES=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_GREENASIA=m +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_WACOM=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_WIIMOTE_EXT=y +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_ZEROPLUS_FF=y +# CONFIG_HID_ZYDACRON is not set +CONFIG_HID_SENSOR_HUB=m + +# +# USB HID support +# +CONFIG_USB_HID=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB_ARCH_HAS_XHCI=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=m +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +# CONFIG_USB_XHCI_HCD_DEBUGGING is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_S5P=y +# CONFIG_USB_EHCI_HCD_PLATFORM is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_EXYNOS=y +# CONFIG_USB_OHCI_HCD_PLATFORM is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_U132_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m +CONFIG_USB_WDM=m +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_REALTEK=m +CONFIG_REALTEK_AUTOPM=y +CONFIG_USB_STORAGE_DATAFAB=m +CONFIG_USB_STORAGE_FREECOM=m +CONFIG_USB_STORAGE_ISD200=m +CONFIG_USB_STORAGE_USBAT=m +CONFIG_USB_STORAGE_SDDR09=m +CONFIG_USB_STORAGE_SDDR55=m +CONFIG_USB_STORAGE_JUMPSHOT=m +CONFIG_USB_STORAGE_ALAUDA=m +CONFIG_USB_STORAGE_ONETOUCH=m +CONFIG_USB_STORAGE_KARMA=m +CONFIG_USB_STORAGE_CYPRESS_ATACB=m +CONFIG_USB_STORAGE_ENE_UB6250=m + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_DWC3=y +# CONFIG_USB_DWC3_HOST is not set +# CONFIG_USB_DWC3_GADGET is not set +CONFIG_USB_DWC3_DUAL_ROLE=y + +# +# Platform Glue Driver Support +# +CONFIG_USB_DWC3_EXYNOS=y + +# +# Debugging features +# +# CONFIG_USB_DWC3_DEBUG is not set +# CONFIG_USB_CHIPIDEA is not set + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CONSOLE=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F81232=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +CONFIG_USB_SERIAL_KEYSPAN_MPR=y +CONFIG_USB_SERIAL_KEYSPAN_USA28=y +CONFIG_USB_SERIAL_KEYSPAN_USA28X=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y +CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y +CONFIG_USB_SERIAL_KEYSPAN_USA19=y +CONFIG_USB_SERIAL_KEYSPAN_USA18X=y +CONFIG_USB_SERIAL_KEYSPAN_USA19W=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y +CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y +CONFIG_USB_SERIAL_KEYSPAN_USA49W=y +CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_METRO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7715_PARPORT=y +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MOTOROLA=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIEMENS_MPI=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_WWAN=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_ZIO=m +CONFIG_USB_SERIAL_WISHBONE=m +CONFIG_USB_SERIAL_ZTE=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +CONFIG_USB_ADUTUX=m +CONFIG_USB_SEVSEG=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +CONFIG_USB_LED=m +CONFIG_USB_CYPRESS_CY7C63=m +CONFIG_USB_CYTHERM=m +CONFIG_USB_IDMOUSE=m +CONFIG_USB_FTDI_ELAN=m +# CONFIG_USB_APPLEDISPLAY is not set +CONFIG_USB_SISUSBVGA=m +# CONFIG_USB_SISUSBVGA_CON is not set +CONFIG_USB_LD=m +CONFIG_USB_TRANCEVIBRATOR=m +CONFIG_USB_IOWARRIOR=m +# CONFIG_USB_TEST is not set +CONFIG_USB_ISIGHTFW=m +CONFIG_USB_YUREX=m +CONFIG_USB_EZUSB_FX2=m +# CONFIG_USB_HSIC_USB3503 is not set +CONFIG_USB_EXYNOS_SWITCH=y +CONFIG_USB_PHY=y +# CONFIG_USB_OTG_WAKELOCK is not set +# CONFIG_NOP_USB_XCEIV is not set +# CONFIG_OMAP_CONTROL_USB is not set +# CONFIG_OMAP_USB3 is not set +CONFIG_SAMSUNG_USBPHY=y +CONFIG_SAMSUNG_USB2PHY=y +CONFIG_SAMSUNG_USB2PHY_DUMMY=y +CONFIG_SAMSUNG_USB3PHY=y +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_RCAR_PHY is not set +# CONFIG_USB_ULPI is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=500 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_LIBCOMPOSITE=m +CONFIG_USB_F_ACM=m +CONFIG_USB_F_SS_LB=m +CONFIG_USB_U_SERIAL=m +CONFIG_USB_F_SERIAL=m +CONFIG_USB_F_OBEX=m +CONFIG_USB_ZERO=m +# CONFIG_USB_ZERO_HNPTEST is not set +CONFIG_USB_AUDIO=m +CONFIG_GADGET_UAC1=y +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +CONFIG_USB_ETH_EEM=y +CONFIG_USB_G_NCM=m +CONFIG_USB_GADGETFS=m +CONFIG_USB_FUNCTIONFS=m +CONFIG_USB_FUNCTIONFS_ETH=y +CONFIG_USB_FUNCTIONFS_RNDIS=y +CONFIG_USB_FUNCTIONFS_GENERIC=y +CONFIG_USB_MASS_STORAGE=m +# CONFIG_USB_GADGET_TARGET is not set +CONFIG_USB_G_SERIAL=m +CONFIG_USB_MIDI_GADGET=m +CONFIG_USB_G_PRINTER=m +CONFIG_USB_CDC_COMPOSITE=m +CONFIG_USB_G_NOKIA=m +CONFIG_USB_G_ACM_MS=m +CONFIG_USB_G_MULTI=m +CONFIG_USB_G_MULTI_RNDIS=y +# CONFIG_USB_G_MULTI_CDC is not set +CONFIG_USB_G_HID=m +# CONFIG_USB_G_DBGP is not set +CONFIG_USB_G_WEBCAM=m +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_CLKGATE=y +CONFIG_MMC_EMBEDDED_SDIO=y +CONFIG_MMC_PARANOID_SD_INIT=y + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set +# CONFIG_MMC_SUPPORT_BKOPS_MODE is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_ARMMMCI is not set +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +CONFIG_MMC_DW=y +CONFIG_MMC_DW_BYPASS_FMP=y +# CONFIG_MMC_DW_FMP_DM_CRYPT is not set +CONFIG_MMC_DW_IDMAC=y +CONFIG_MMC_DW_PLTFM=y +CONFIG_MMC_DW_EXYNOS=y +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_BLINKM is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_GPIO=y +# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set + +# +# iptables trigger is under Netfilter config (LED target) +# +CONFIG_LEDS_TRIGGER_TRANSIENT=y +# CONFIG_LEDS_TRIGGER_CAMERA is not set +CONFIG_SWITCH=y +# CONFIG_SWITCH_GPIO is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_DS2404 is not set + +# +# on-CPU RTC drivers +# +CONFIG_HAVE_S3C_RTC=y +# CONFIG_RTC_DRV_S3C is not set +CONFIG_RTC_DRV_SEC=y +# CONFIG_RTC_DRV_PL030 is not set +# CONFIG_RTC_DRV_PL031 is not set +# CONFIG_RTC_DRV_SNVS is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +# CONFIG_AMBA_PL08X is not set +# CONFIG_DW_DMAC is not set +# CONFIG_TIMB_DMA is not set +CONFIG_PL330_DMA=y +# CONFIG_PL330TEST_LOG is not set +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y + +# +# DMA Clients +# +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VFIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +CONFIG_USBIP_CORE=m +CONFIG_USBIP_VHCI_HCD=m +# CONFIG_USBIP_HOST is not set +# CONFIG_USBIP_DEBUG is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_PANEL is not set +CONFIG_R8712U=m +CONFIG_RTS5139=m +# CONFIG_RTS5139_DEBUG is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set + +# +# IIO staging drivers +# + +# +# Accelerometers +# +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +# Direct Digital Synthesis +# +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16060 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16260 is not set + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_HMC5843 is not set + +# +# Active energy metering IC +# +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + +# +# Triggers - standalone +# +# CONFIG_IIO_SIMPLE_DUMMY is not set +CONFIG_ZSMALLOC=y +CONFIG_ZRAM=m +CONFIG_ZRAM_DEBUG=y +CONFIG_USB_ENESTORAGE=m +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +# CONFIG_ANDROID_BINDER_IPC is not set +# CONFIG_ASHMEM is not set +# CONFIG_ANDROID_LOGGER is not set +CONFIG_ANDROID_TIMED_OUTPUT=y +# CONFIG_ANDROID_TIMED_GPIO is not set +# CONFIG_ANDROID_LOW_MEMORY_KILLER is not set +# CONFIG_ANDROID_INTF_ALARM_DEV is not set +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +# CONFIG_ION_TEST is not set +CONFIG_ION_EXYNOS=y +CONFIG_ION_EXYNOS_OF=y +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_CED1401 is not set +# CONFIG_DGRP is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +CONFIG_COMMON_CLK_DEBUG=y +# CONFIG_COMMON_CLK_SI5351 is not set + +# +# Hardware Spinlock drivers +# +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_EXYNOS_MCT=y +# CONFIG_MAILBOX is not set +CONFIG_IOMMU_API=y +CONFIG_IOMMU_SUPPORT=y +CONFIG_OF_IOMMU=y +CONFIG_EXYNOS_IOMMU=y +CONFIG_EXYNOS5_IOMMU=y +# CONFIG_EXYNOS7_IOMMU is not set +CONFIG_EXYNOS_IOVMM=y +CONFIG_EXYNOS_IOMMU_NO_MASTER_CLKGATE=y +# CONFIG_EXYNOS_IOMMU_DEBUG is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# +CONFIG_PM_DEVFREQ=y + +# +# DEVFREQ Governors +# +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +# CONFIG_DEVFREQ_GOV_SIMPLE_USAGE is not set +CONFIG_DEVFREQ_GOV_SIMPLE_EXYNOS=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +# CONFIG_DEVFREQ_GOV_POWERSAVE is not set +# CONFIG_DEVFREQ_GOV_USERSPACE is not set + +# +# DEVFREQ Drivers +# +CONFIG_ARM_EXYNOS5422_BUS_DEVFREQ=y +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +CONFIG_IIO=y +# CONFIG_IIO_BUFFER is not set +# CONFIG_IIO_TRIGGER is not set + +# +# Accelerometers +# +# CONFIG_HID_SENSOR_ACCEL_3D is not set +# CONFIG_KXSD9 is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7887 is not set +CONFIG_EXYNOS_ADC=y +# CONFIG_MAX1363 is not set +# CONFIG_TI_ADC081C is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# Hid Sensor IIO Common +# +# CONFIG_HID_SENSOR_IIO_COMMON is not set + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5686 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_HID_SENSOR_GYRO_3D is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set + +# +# Inertial measurement units +# +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_INV_MPU6050_IIO is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_VCNL4000 is not set +# CONFIG_HID_SENSOR_ALS is not set + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_HID_SENSOR_MAGNETOMETER_3D is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +CONFIG_PWM=y +CONFIG_PWM_SAMSUNG=y +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_MOBICORE_DRIVER is not set + +# +# ODROID Specific Hardware +# +CONFIG_ODROID_EXYNOS5_FAN=y +CONFIG_ODROID_EXYNOS5_SP=m +CONFIG_ODROID_EXYNOS5_IOBOARD=m +# CONFIG_ODROID_EXYNOS5_IOBOARD_DEBUG is not set +CONFIG_ODROID_EXYNOS5_HDMI_PHY_TUNE=y +# CONFIG_ODROID_EXYNOS5_HDMI_PHY_TUNE_DEBUG is not set + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +# CONFIG_PHY_EXYNOS_MIPI_VIDEO is not set +CONFIG_PHY_EXYNOS_DP_VIDEO=y +# CONFIG_BCM_KONA_USB2_PHY is not set +# CONFIG_PHY_SAMSUNG_USB2 is not set +# CONFIG_PHY_EXYNOS5_USBDRD is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT23=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +CONFIG_JFS_FS=m +CONFIG_JFS_POSIX_ACL=y +CONFIG_JFS_SECURITY=y +# CONFIG_JFS_DEBUG is not set +CONFIG_JFS_STATISTICS=y +# CONFIG_XFS_FS is not set +CONFIG_GFS2_FS=m +CONFIG_OCFS2_FS=m +CONFIG_OCFS2_FS_O2CB=m +CONFIG_OCFS2_FS_STATS=y +CONFIG_OCFS2_DEBUG_MASKLOG=y +# CONFIG_OCFS2_DEBUG_FS is not set +CONFIG_BTRFS_FS=m +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_BTRFS_FS_CHECK_INTEGRITY=y +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BTRFS_DEBUG is not set +CONFIG_NILFS2_FS=m +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +CONFIG_QUOTA=y +CONFIG_QUOTA_NETLINK_INTERFACE=y +CONFIG_PRINT_QUOTA_WARNING=y +# CONFIG_QUOTA_DEBUG is not set +CONFIG_QUOTA_TREE=m +# CONFIG_QFMT_V1 is not set +# CONFIG_QFMT_V2 is not set +CONFIG_QUOTACTL=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=m +CONFIG_CUSE=m +CONFIG_GENERIC_ACL=y + +# +# Caches +# +CONFIG_FSCACHE=m +# CONFIG_FSCACHE_STATS is not set +# CONFIG_FSCACHE_HISTOGRAM is not set +# CONFIG_FSCACHE_DEBUG is not set +# CONFIG_FSCACHE_OBJECT_LIST is not set +# CONFIG_CACHEFILES is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +# CONFIG_PROC_PAGE_MONITOR is not set +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +CONFIG_HFS_FS=m +CONFIG_HFSPLUS_FS=m +CONFIG_BEFS_FS=m +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_LOGFS=m +CONFIG_CRAMFS=y +CONFIG_SQUASHFS=m +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_ZLIB=y +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +CONFIG_MINIX_FS=m +# CONFIG_OMFS_FS is not set +CONFIG_HPFS_FS=m +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +CONFIG_ROMFS_FS=y +CONFIG_ROMFS_BACKED_BY_BLOCK=y +CONFIG_ROMFS_ON_BLOCK=y +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +CONFIG_UFS_FS=m +CONFIG_UFS_FS_WRITE=y +# CONFIG_UFS_DEBUG is not set +CONFIG_F2FS_FS=m +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +# CONFIG_AUFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V2=m +CONFIG_NFS_V3=m +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=m +CONFIG_NFS_SWAP=y +CONFIG_NFS_V4_1=y +CONFIG_PNFS_FILE_LAYOUT=m +CONFIG_PNFS_BLOCK=m +CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" +# CONFIG_NFS_FSCACHE is not set +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +# CONFIG_NFSD_FAULT_INJECTION is not set +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=m +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +CONFIG_SUNRPC_GSS=m +CONFIG_SUNRPC_BACKCHANNEL=y +CONFIG_SUNRPC_SWAP=y +CONFIG_RPCSEC_GSS_KRB5=m +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=m +CONFIG_CIFS_STATS=y +CONFIG_CIFS_STATS2=y +CONFIG_CIFS_WEAK_PW_HASH=y +CONFIG_CIFS_UPCALL=y +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_ACL=y +# CONFIG_CIFS_DEBUG is not set +CONFIG_CIFS_DFS_UPCALL=y +CONFIG_CIFS_SMB2=y +# CONFIG_CIFS_FSCACHE is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_MAC_ROMAN=m +CONFIG_NLS_MAC_CELTIC=m +CONFIG_NLS_MAC_CENTEURO=m +CONFIG_NLS_MAC_CROATIAN=m +CONFIG_NLS_MAC_CYRILLIC=m +CONFIG_NLS_MAC_GAELIC=m +CONFIG_NLS_MAC_GREEK=m +CONFIG_NLS_MAC_ICELAND=m +CONFIG_NLS_MAC_INUIT=m +CONFIG_NLS_MAC_ROMANIAN=m +CONFIG_NLS_MAC_TURKISH=m +CONFIG_NLS_UTF8=m +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_PRINTK_CORE_NUM=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_LOCKUP_DETECTOR=y +CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU=y +CONFIG_HARDLOCKUP_DETECTOR=y +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=1 +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1 +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +CONFIG_DEBUG_PREEMPT=y +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_PI_LIST=y +# CONFIG_RT_MUTEX_TESTER is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_LOCK_ALLOC=y +# CONFIG_PROVE_LOCKING is not set +CONFIG_LOCKDEP=y +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_LOCKDEP is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_DEBUG_LIST=y +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU_DELAY is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACE_CLOCK=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_GRAPH_TRACER=y +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_FTRACE_SYSCALLS is not set +# CONFIG_TRACER_SNAPSHOT is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_PROBE_EVENTS is not set +CONFIG_DYNAMIC_FTRACE=y +# CONFIG_FUNCTION_PROFILER is not set +CONFIG_FTRACE_MCOUNT_RECORD=y +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_ASYNC_RAID6_TEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_ARM_UNWIND is not set +CONFIG_OLD_MCOUNT=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_RODATA is not set +# CONFIG_DEBUG_LL is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_UNCOMPRESS_INCLUDE="mach/uncompress.h" +# CONFIG_OC_ETM is not set +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_ENCRYPTED_KEYS is not set +# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_XOR_BLOCKS=m +CONFIG_ASYNC_CORE=m +CONFIG_ASYNC_MEMCPY=m +CONFIG_ASYNC_XOR=m +CONFIG_ASYNC_PQ=m +CONFIG_ASYNC_RAID6_RECOV=m +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=m +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_PCRYPT=m +CONFIG_CRYPTO_WORKQUEUE=y +CONFIG_CRYPTO_CRYPTD=m +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_SEQIV=y + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=m +CONFIG_CRYPTO_ECB=y +CONFIG_CRYPTO_LRW=m +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=y + +# +# Hash modes +# +CONFIG_CRYPTO_CMAC=m +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_XCBC=m +CONFIG_CRYPTO_VMAC=m + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_CRC32=m +CONFIG_CRYPTO_GHASH=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_RMD128=m +CONFIG_CRYPTO_RMD160=m +CONFIG_CRYPTO_RMD256=m +CONFIG_CRYPTO_RMD320=m +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA1_ARM=m +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_AES_ARM=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=y +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_BLOWFISH_COMMON=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST_COMMON=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_SALSA20=m +CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_ZLIB=m +CONFIG_CRYPTO_LZO=m + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_USER_API=m +CONFIG_CRYPTO_USER_API_HASH=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_S5P_DEV_ACE=y +# CONFIG_ACE_USE_SSS_VER_5 is not set +CONFIG_ACE_USE_SLIMSSS_VER_1=y +# CONFIG_ACE_USE_CCI is not set +CONFIG_ACE_BC=y +CONFIG_ACE_BC_ASYNC=y +# CONFIG_ACE_BC_IRQMODE is not set +CONFIG_ACE_HASH_SHA1=y +CONFIG_ACE_HASH_SHA256=y +# CONFIG_ACE_DEBUG is not set +# CONFIG_ASYMMETRIC_KEY_TYPE is not set +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_RAID6_PQ=m +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_XZ_DEC=y +# CONFIG_XZ_DEC_X86 is not set +# CONFIG_XZ_DEC_POWERPC is not set +# CONFIG_XZ_DEC_IA64 is not set +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_BTREE=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_OID_REGISTRY=m +# CONFIG_VIRTUALIZATION is not set +CONFIG_BACKPORT_INTEGRATE=y +CONFIG_BACKPORT_DIR="backports" +CONFIG_BACKPORT_VERSION="backports-20150222-0-g2745ccd" +CONFIG_BACKPORT_KERNEL_VERSION="next-20150222-0-g53e418f1" +CONFIG_BACKPORT_KERNEL_NAME="Linux" +CONFIG_BACKPORT_LINUX=y +CONFIG_BACKPORT_KERNEL_3_11=y +CONFIG_BACKPORT_KERNEL_3_12=y +CONFIG_BACKPORT_KERNEL_3_13=y +CONFIG_BACKPORT_KERNEL_3_14=y +CONFIG_BACKPORT_KERNEL_3_15=y +CONFIG_BACKPORT_KERNEL_3_16=y +CONFIG_BACKPORT_KERNEL_3_17=y +CONFIG_BACKPORT_KERNEL_3_18=y +CONFIG_BACKPORT_KERNEL_3_19=y +CONFIG_BACKPORT_KERNEL_3_20=y +CONFIG_BACKPORT_KERNEL_3_21=y +CONFIG_BACKPORT_KERNEL_3_22=y +CONFIG_BACKPORT_KERNEL_3_23=y +CONFIG_BACKPORT_KERNEL_3_24=y +CONFIG_BACKPORT_KERNEL_3_25=y +CONFIG_BACKPORT_KERNEL_3_26=y +CONFIG_BACKPORT_KERNEL_3_27=y +CONFIG_BACKPORT_KERNEL_3_28=y +CONFIG_BACKPORT_KERNEL_3_29=y +CONFIG_BACKPORT_KERNEL_3_30=y +CONFIG_BACKPORT_KERNEL_3_31=y +CONFIG_BACKPORT_KERNEL_3_32=y +CONFIG_BACKPORT_KERNEL_3_33=y +CONFIG_BACKPORT_KERNEL_3_34=y +CONFIG_BACKPORT_KERNEL_3_35=y +CONFIG_BACKPORT_KERNEL_3_36=y +CONFIG_BACKPORT_KERNEL_3_37=y +CONFIG_BACKPORT_KERNEL_3_38=y +CONFIG_BACKPORT_KERNEL_3_39=y +CONFIG_BACKPORT_KERNEL_3_40=y +CONFIG_BACKPORT_KERNEL_3_41=y +CONFIG_BACKPORT_KERNEL_3_42=y +CONFIG_BACKPORT_KERNEL_3_43=y +CONFIG_BACKPORT_KERNEL_3_44=y +CONFIG_BACKPORT_KERNEL_3_45=y +CONFIG_BACKPORT_KERNEL_3_46=y +CONFIG_BACKPORT_KERNEL_3_47=y +CONFIG_BACKPORT_KERNEL_3_48=y +CONFIG_BACKPORT_KERNEL_3_49=y +CONFIG_BACKPORT_KERNEL_3_50=y +CONFIG_BACKPORT_KERNEL_3_51=y +CONFIG_BACKPORT_KERNEL_3_52=y +CONFIG_BACKPORT_KERNEL_3_53=y +CONFIG_BACKPORT_KERNEL_3_54=y +CONFIG_BACKPORT_KERNEL_3_55=y +CONFIG_BACKPORT_KERNEL_3_56=y +CONFIG_BACKPORT_KERNEL_3_57=y +CONFIG_BACKPORT_KERNEL_3_58=y +CONFIG_BACKPORT_KERNEL_3_59=y +CONFIG_BACKPORT_KERNEL_3_60=y +CONFIG_BACKPORT_KERNEL_3_61=y +CONFIG_BACKPORT_KERNEL_3_62=y +CONFIG_BACKPORT_KERNEL_3_63=y +CONFIG_BACKPORT_KERNEL_3_64=y +CONFIG_BACKPORT_KERNEL_3_65=y +CONFIG_BACKPORT_KERNEL_3_66=y +CONFIG_BACKPORT_KERNEL_3_67=y +CONFIG_BACKPORT_KERNEL_3_68=y +CONFIG_BACKPORT_KERNEL_3_69=y +CONFIG_BACKPORT_KERNEL_3_70=y +CONFIG_BACKPORT_KERNEL_3_71=y +CONFIG_BACKPORT_KERNEL_3_72=y +CONFIG_BACKPORT_KERNEL_3_73=y +CONFIG_BACKPORT_KERNEL_3_74=y +CONFIG_BACKPORT_KERNEL_3_75=y +CONFIG_BACKPORT_KERNEL_3_76=y +CONFIG_BACKPORT_KERNEL_3_77=y +CONFIG_BACKPORT_KERNEL_3_78=y +CONFIG_BACKPORT_KERNEL_3_79=y +CONFIG_BACKPORT_KERNEL_3_80=y +CONFIG_BACKPORT_KERNEL_3_81=y +CONFIG_BACKPORT_KERNEL_3_82=y +CONFIG_BACKPORT_KERNEL_3_83=y +CONFIG_BACKPORT_KERNEL_3_84=y +CONFIG_BACKPORT_KERNEL_3_85=y +CONFIG_BACKPORT_KERNEL_3_86=y +CONFIG_BACKPORT_KERNEL_3_87=y +CONFIG_BACKPORT_KERNEL_3_88=y +CONFIG_BACKPORT_KERNEL_3_89=y +CONFIG_BACKPORT_KERNEL_3_90=y +CONFIG_BACKPORT_KERNEL_3_91=y +CONFIG_BACKPORT_KERNEL_3_92=y +CONFIG_BACKPORT_KERNEL_3_93=y +CONFIG_BACKPORT_KERNEL_3_94=y +CONFIG_BACKPORT_KERNEL_3_95=y +CONFIG_BACKPORT_KERNEL_3_96=y +CONFIG_BACKPORT_KERNEL_3_97=y +CONFIG_BACKPORT_KERNEL_3_98=y +CONFIG_BACKPORT_BPAUTO_BUILD_CORDIC=m +CONFIG_BACKPORT_BPAUTO_USERSEL_BUILD_ALL=y +# CONFIG_BACKPORT_BPAUTO_BUILD_CRYPTO_CCM is not set +CONFIG_BACKPORT_CFG80211=m +# CONFIG_BACKPORT_NL80211_TESTMODE is not set +# CONFIG_BACKPORT_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_BACKPORT_CFG80211_REG_DEBUG is not set +# CONFIG_BACKPORT_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_BACKPORT_CFG80211_DEFAULT_PS=y +# CONFIG_BACKPORT_CFG80211_DEBUGFS is not set +# CONFIG_BACKPORT_CFG80211_INTERNAL_REGDB is not set +# CONFIG_BACKPORT_CFG80211_WEXT is not set +# CONFIG_BACKPORT_LIB80211 is not set +CONFIG_BACKPORT_MAC80211=m +CONFIG_BACKPORT_MAC80211_HAS_RC=y +CONFIG_BACKPORT_MAC80211_RC_MINSTREL=y +CONFIG_BACKPORT_MAC80211_RC_MINSTREL_HT=y +# CONFIG_BACKPORT_MAC80211_RC_MINSTREL_VHT is not set +CONFIG_BACKPORT_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_BACKPORT_MAC80211_RC_DEFAULT="minstrel_ht" +# CONFIG_BACKPORT_MAC80211_MESH is not set +CONFIG_BACKPORT_MAC80211_LEDS=y +# CONFIG_BACKPORT_MAC80211_DEBUGFS is not set +# CONFIG_BACKPORT_MAC80211_MESSAGE_TRACING is not set +# CONFIG_BACKPORT_MAC80211_DEBUG_MENU is not set +# CONFIG_BACKPORT_BT is not set +CONFIG_BACKPORT_WLAN=y +# CONFIG_BACKPORT_LIBERTAS_THINFIRM is not set +CONFIG_BACKPORT_AT76C50X_USB=m +CONFIG_BACKPORT_USB_NET_RNDIS_WLAN=m +CONFIG_BACKPORT_RTL8187=m +# CONFIG_BACKPORT_MAC80211_HWSIM is not set +CONFIG_BACKPORT_ATH_COMMON=m +CONFIG_BACKPORT_ATH_CARDS=m +# CONFIG_BACKPORT_ATH_DEBUG is not set +CONFIG_BACKPORT_ATH9K_HW=m +CONFIG_BACKPORT_ATH9K_COMMON=m +CONFIG_BACKPORT_ATH9K_BTCOEX_SUPPORT=y +CONFIG_BACKPORT_ATH9K=m +# CONFIG_BACKPORT_ATH9K_AHB is not set +# CONFIG_BACKPORT_ATH9K_DEBUGFS is not set +# CONFIG_BACKPORT_ATH9K_DYNACK is not set +# CONFIG_BACKPORT_ATH9K_WOW is not set +# CONFIG_BACKPORT_ATH9K_CHANNEL_CONTEXT is not set +CONFIG_BACKPORT_ATH9K_PCOEM=y +CONFIG_BACKPORT_ATH9K_HTC=m +# CONFIG_BACKPORT_ATH9K_HTC_DEBUGFS is not set +CONFIG_BACKPORT_CARL9170=m +CONFIG_BACKPORT_CARL9170_LEDS=y +CONFIG_BACKPORT_CARL9170_WPC=y +# CONFIG_BACKPORT_CARL9170_HWRNG is not set +CONFIG_BACKPORT_ATH6KL=m +# CONFIG_BACKPORT_ATH6KL_SDIO is not set +CONFIG_BACKPORT_ATH6KL_USB=m +# CONFIG_BACKPORT_ATH6KL_DEBUG is not set +# CONFIG_BACKPORT_ATH6KL_TRACING is not set +CONFIG_BACKPORT_AR5523=m +CONFIG_BACKPORT_ATH10K=m +# CONFIG_BACKPORT_ATH10K_DEBUG is not set +# CONFIG_BACKPORT_ATH10K_DEBUGFS is not set +# CONFIG_BACKPORT_ATH10K_TRACING is not set +CONFIG_BACKPORT_WCN36XX=m +# CONFIG_BACKPORT_WCN36XX_DEBUGFS is not set +CONFIG_BACKPORT_BRCMUTIL=m +CONFIG_BACKPORT_BRCMFMAC=m +CONFIG_BACKPORT_BRCMFMAC_PROTO_BCDC=y +# CONFIG_BACKPORT_BRCMFMAC_SDIO is not set +CONFIG_BACKPORT_BRCMFMAC_USB=y +# CONFIG_BACKPORT_LIBERTAS is not set +CONFIG_BACKPORT_P54_COMMON=m +CONFIG_BACKPORT_P54_USB=m +# CONFIG_BACKPORT_P54_SPI is not set +CONFIG_BACKPORT_RT2X00=m +CONFIG_BACKPORT_RT2500USB=m +CONFIG_BACKPORT_RT73USB=m +CONFIG_BACKPORT_RT2800USB=m +CONFIG_BACKPORT_RT2800USB_RT33XX=y +CONFIG_BACKPORT_RT2800USB_RT35XX=y +CONFIG_BACKPORT_RT2800USB_RT3573=y +CONFIG_BACKPORT_RT2800USB_RT53XX=y +CONFIG_BACKPORT_RT2800USB_RT55XX=y +CONFIG_BACKPORT_RT2800USB_UNKNOWN=y +CONFIG_BACKPORT_RT2800_LIB=m +CONFIG_BACKPORT_RT2X00_LIB_USB=m +CONFIG_BACKPORT_RT2X00_LIB=m +CONFIG_BACKPORT_RT2X00_LIB_FIRMWARE=y +CONFIG_BACKPORT_RT2X00_LIB_CRYPTO=y +CONFIG_BACKPORT_RT2X00_LIB_LEDS=y +# CONFIG_BACKPORT_RT2X00_DEBUG is not set +CONFIG_BACKPORT_RTL_CARDS=m +# CONFIG_BACKPORT_RTL8192CU is not set +CONFIG_BACKPORT_RTL8192C_COMMON=m +# CONFIG_BACKPORT_WL_TI is not set +CONFIG_BACKPORT_ZD1211RW=m +# CONFIG_BACKPORT_ZD1211RW_DEBUG is not set +# CONFIG_BACKPORT_MWIFIEX is not set +# CONFIG_BACKPORT_RSI_91X is not set +CONFIG_BACKPORT_USB_NET_DRIVERS=m +CONFIG_BACKPORT_USB_USBNET=m +CONFIG_BACKPORT_USB_NET_CDCETHER=m +# CONFIG_BACKPORT_USB_NET_CDC_NCM is not set +# CONFIG_BACKPORT_USB_NET_CDC_MBIM is not set +CONFIG_BACKPORT_USB_NET_RNDIS_HOST=m +# CONFIG_BACKPORT_USB_NET_QMI_WWAN is not set +# CONFIG_BACKPORT_USB_SIERRA_NET is not set +# CONFIG_BACKPORT_NFC is not set +# CONFIG_BACKPORT_NFC_DIGITAL is not set +# CONFIG_BACKPORT_NFC_NCI is not set +# CONFIG_BACKPORT_NFC_NCI_SPI is not set +# CONFIG_BACKPORT_NFC_HCI is not set +# CONFIG_BACKPORT_NFC_SHDLC is not set +# CONFIG_BACKPORT_6LOWPAN is not set +# CONFIG_BACKPORT_IEEE802154 is not set + +# +# USB Device Class drivers +# +CONFIG_BACKPORT_USB_WDM=m +CONFIG_RTL8192CU=m +CONFIG_RTL8821AU=m +CONFIG_RTL8188EU=m +CONFIG_RTL8192DU=m diff --git a/config/linux-s500.config b/config/linux-s500.config new file mode 100644 index 000000000..f77b0cdda --- /dev/null +++ b/config/linux-s500.config @@ -0,0 +1,3738 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 3.10.37 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_ARCH_HAS_CPUFREQ=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_FHANDLE=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y +CONFIG_AUDIT_WATCH=y +CONFIG_AUDIT_TREE=y +# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set +CONFIG_HAVE_GENERIC_HARDIRQS=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_IRQ_DOMAIN=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_KTIME_SCALAR=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set + +# +# RCU Subsystem +# +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_PREEMPT_RCU=y +CONFIG_RCU_STALL_COMMON=y +# CONFIG_RCU_USER_QS is not set +CONFIG_RCU_FANOUT=32 +CONFIG_RCU_FANOUT_LEAF=16 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_RCU_FAST_NO_HZ is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_BOOST is not set +# CONFIG_RCU_NOCB_CPU is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_CGROUPS=y +CONFIG_CGROUP_DEBUG=y +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_DEVICE is not set +# CONFIG_CPUSETS is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_MEMCG=y +# CONFIG_MEMCG_SWAP is not set +# CONFIG_MEMCG_KMEM is not set +# CONFIG_CGROUP_PERF is not set +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +# CONFIG_CFS_BANDWIDTH is not set +CONFIG_RT_GROUP_SCHED=y +# CONFIG_BLK_CGROUP is not set +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +# CONFIG_USER_NS is not set +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_UIDGID_CONVERTED=y +# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set +# CONFIG_SCHED_AUTOGROUP is not set +CONFIG_MM_OWNER=y +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_HOTPLUG=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EXPERT=y +CONFIG_UID16=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_EMBEDDED=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +CONFIG_DEBUG_PERF_USE_VMALLOC=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_TRACEPOINTS=y +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_USE_GENERIC_SMP_HELPERS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_SECCOMP_FILTER=y +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +# CONFIG_BLK_DEV_BSGLIB is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_UNINLINE_SPIN_UNLOCK=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_MULTIPLATFORM is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P64X0 is not set +# CONFIG_ARCH_S5PC100 is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set +CONFIG_ARCH_OWL=y +# CONFIG_GPIO_PCA953X is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_PLAT_SPEAR is not set + +# +# Owl configurations +# +CONFIG_MACH_OWL=y +# CONFIG_OWL_DEBUG_IRQ_STACK is not set +CONFIG_SERIAL_OWL=y +CONFIG_SERIAL_OWL_CONSOLE=y +# CONFIG_OWL_CONSOLE_KEEP_ON is not set +CONFIG_GPIO_OWL=y +CONFIG_GPU_RESERVED_SIZE=64 +CONFIG_ION_RESERVED_SIZE=96 +CONFIG_FB_RESERVED_SIZE=32 +CONFIG_I2C_OWL=y +CONFIG_I2C_OWL_BUS0=y +CONFIG_I2C_OWL_BUS1=y +# CONFIG_I2C_OWL_BUS2 is not set +# CONFIG_I2C_OWL_BUS3 is not set +CONFIG_PINCTRL_OWL=y +CONFIG_ACTIONS_SOC_CAMERA=y +# CONFIG_FB_ACTIONS is not set +# CONFIG_BACKLIGHT_ACTIONS is not set +CONFIG_CPUFREQ_ACTIONS=y +CONFIG_OWL_CLOSE_PATCH=y + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +CONFIG_ARM_THUMBEE=y +CONFIG_ARM_VIRT_EXT=y +# CONFIG_SWP_EMULATE is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +CONFIG_CACHE_PL310=y +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_NR_BANKS=8 +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +# CONFIG_ARM_ERRATA_458693 is not set +# CONFIG_ARM_ERRATA_460075 is not set +# CONFIG_ARM_ERRATA_742230 is not set +# CONFIG_ARM_ERRATA_742231 is not set +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_ARM_ERRATA_643719 is not set +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_ARM_ERRATA_743622 is not set +# CONFIG_ARM_ERRATA_751472 is not set +# CONFIG_PL310_ERRATA_753970 is not set +CONFIG_ARM_ERRATA_754322=y +# CONFIG_ARM_ERRATA_754327 is not set +CONFIG_ARM_ERRATA_764369=y +# CONFIG_PL310_ERRATA_769419 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_FIQ_DEBUGGER is not set + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +CONFIG_SCHED_MC=y +# CONFIG_SCHED_SMT is not set +# CONFIG_DISABLE_CPU_SCHED_DOMAIN_BALANCE is not set +CONFIG_HAVE_ARM_SCU=y +# CONFIG_HAVE_ARM_ARCH_TIMER is not set +CONFIG_HAVE_ARM_TWD=y +# CONFIG_MCPM is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +# CONFIG_ARM_PSCI is not set +CONFIG_LOCAL_TIMERS=y +CONFIG_ARCH_NR_GPIO=0 +# CONFIG_PREEMPT_NONE is not set +# CONFIG_PREEMPT_VOLUNTARY is not set +CONFIG_PREEMPT=y +CONFIG_PREEMPT_COUNT=y +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +# CONFIG_HIGHPTE is not set +CONFIG_HW_PERF_EVENTS=y +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_MEMORY_ISOLATION=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +# CONFIG_BOUNCE is not set +CONFIG_VIRT_TO_BUS=y +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CROSS_MEMORY_ATTACH=y +# CONFIG_CLEANCACHE is not set +# CONFIG_FRONTSWAP is not set +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +CONFIG_SECCOMP=y +# CONFIG_CC_STACKPROTECTOR is not set +# CONFIG_XEN is not set +# CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART is not set + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +# CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +# CONFIG_ARM_APPENDED_DTB is not set +CONFIG_CMDLINE="" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +# CONFIG_AUTO_ZRELADDR is not set + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_INTERACTIVE=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y +# CONFIG_GENERIC_CPUFREQ_CPU0 is not set + +# +# ARM CPU frequency scaling drivers +# +# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set +# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set +# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +CONFIG_ARM_OWL_CPUFREQ=y +CONFIG_CPU_IDLE=y +# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_EARLYSUSPEND=y +CONFIG_FB_EARLYSUSPEND=y +CONFIG_HIBERNATE_CALLBACKS=y +CONFIG_HIBERNATION=y +CONFIG_PM_STD_PARTITION="" +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +CONFIG_PM_AUTOSLEEP=y +CONFIG_PM_WAKELOCKS=y +CONFIG_PM_WAKELOCKS_LIMIT=0 +# CONFIG_PM_WAKELOCKS_GC is not set +CONFIG_PM_RUNTIME=y +CONFIG_PM=y +CONFIG_PM_DEBUG=y +# CONFIG_PM_ADVANCED_DEBUG is not set +# CONFIG_PM_TEST_SUSPEND is not set +CONFIG_PM_SLEEP_DEBUG=y +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_HAS_OPP=y +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_CPU_PM=y +CONFIG_SUSPEND_TIME=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_DIAG is not set +CONFIG_UNIX=y +# CONFIG_UNIX_DIAG is not set +CONFIG_XFRM=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +CONFIG_XFRM_MIGRATE=y +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +CONFIG_NET_KEY_MIGRATE=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +# CONFIG_IP_FIB_TRIE_STATS is not set +CONFIG_IP_MULTIPLE_TABLES=y +# CONFIG_IP_ROUTE_MULTIPATH is not set +# CONFIG_IP_ROUTE_VERBOSE is not set +CONFIG_IP_ROUTE_CLASSID=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE_DEMUX is not set +CONFIG_NET_IP_TUNNEL=y +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_NET_IPVTI is not set +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_TUNNEL=m +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_INET_UDP_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_INET6_XFRM_TUNNEL=y +CONFIG_INET6_TUNNEL=y +CONFIG_INET6_XFRM_MODE_TRANSPORT=y +CONFIG_INET6_XFRM_MODE_TUNNEL=y +CONFIG_INET6_XFRM_MODE_BEET=y +# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set +CONFIG_IPV6_SIT=y +# CONFIG_IPV6_SIT_6RD is not set +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=y +# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +# CONFIG_IPV6_SUBTREES is not set +# CONFIG_IPV6_MROUTE is not set +# CONFIG_NETLABEL is not set +# CONFIG_ANDROID_PARANOID_NETWORK is not set +CONFIG_NET_ACTIVITY_STATS=y +CONFIG_NETWORK_SECMARK=y +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=y + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +CONFIG_NETFILTER_NETLINK_ACCT=y +CONFIG_NETFILTER_NETLINK_QUEUE=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_MARK=y +# CONFIG_NF_CONNTRACK_SECMARK is not set +# CONFIG_NF_CONNTRACK_ZONES is not set +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +# CONFIG_NF_CONNTRACK_TIMEOUT is not set +# CONFIG_NF_CONNTRACK_TIMESTAMP is not set +CONFIG_NF_CONNTRACK_LABELS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_GRE=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_H323=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_BROADCAST=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +# CONFIG_NF_CONNTRACK_SNMP is not set +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +# CONFIG_NF_CONNTRACK_SIP is not set +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NF_CT_NETLINK=y +# CONFIG_NF_CT_NETLINK_TIMEOUT is not set +# CONFIG_NETFILTER_NETLINK_QUEUE_CT is not set +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PROTO_DCCP=m +CONFIG_NF_NAT_PROTO_UDPLITE=m +CONFIG_NF_NAT_PROTO_SCTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +# CONFIG_NF_NAT_SIP is not set +CONFIG_NF_NAT_TFTP=m +CONFIG_NETFILTER_TPROXY=y +CONFIG_NETFILTER_XTABLES=y + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=y +CONFIG_NETFILTER_XT_CONNMARK=y + +# +# Xtables targets +# +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set +# CONFIG_NETFILTER_XT_TARGET_CHECKSUM is not set +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y +CONFIG_NETFILTER_XT_TARGET_CONNMARK=y +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=y +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y +# CONFIG_NETFILTER_XT_TARGET_LED is not set +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=y +CONFIG_NETFILTER_XT_TARGET_NETMAP=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=y +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_REDIRECT=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=y +CONFIG_NETFILTER_XT_TARGET_TRACE=y +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=y +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m + +# +# Xtables matches +# +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y +CONFIG_NETFILTER_XT_MATCH_CONNMARK=y +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ECN=y +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y +CONFIG_NETFILTER_XT_MATCH_HELPER=y +CONFIG_NETFILTER_XT_MATCH_HL=y +CONFIG_NETFILTER_XT_MATCH_IPRANGE=y +CONFIG_NETFILTER_XT_MATCH_LENGTH=y +CONFIG_NETFILTER_XT_MATCH_LIMIT=y +CONFIG_NETFILTER_XT_MATCH_MAC=y +CONFIG_NETFILTER_XT_MATCH_MARK=y +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=y +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y +CONFIG_NETFILTER_XT_MATCH_QUOTA=y +CONFIG_NETFILTER_XT_MATCH_QUOTA2=y +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=y +CONFIG_NETFILTER_XT_MATCH_STATE=y +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y +CONFIG_NETFILTER_XT_MATCH_STRING=y +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=y +CONFIG_NETFILTER_XT_MATCH_U32=y +# CONFIG_IP_SET is not set +# CONFIG_IP_VS is not set + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=y +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_CONNTRACK_PROC_COMPAT=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_RPFILTER=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_REJECT_SKERR=y +CONFIG_IP_NF_TARGET_ULOG=y +CONFIG_NF_NAT_IPV4=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=y +CONFIG_IP_NF_TARGET_TTL=y +CONFIG_IP_NF_RAW=y +CONFIG_IP_NF_SECURITY=y +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=y +CONFIG_NF_CONNTRACK_IPV6=y +CONFIG_IP6_NF_IPTABLES=y +# CONFIG_IP6_NF_MATCH_AH is not set +# CONFIG_IP6_NF_MATCH_EUI64 is not set +# CONFIG_IP6_NF_MATCH_FRAG is not set +# CONFIG_IP6_NF_MATCH_OPTS is not set +# CONFIG_IP6_NF_MATCH_HL is not set +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set +# CONFIG_IP6_NF_MATCH_MH is not set +# CONFIG_IP6_NF_MATCH_RPFILTER is not set +# CONFIG_IP6_NF_MATCH_RT is not set +# CONFIG_IP6_NF_TARGET_HL is not set +CONFIG_IP6_NF_FILTER=y +CONFIG_IP6_NF_TARGET_REJECT=y +CONFIG_IP6_NF_TARGET_REJECT_SKERR=y +CONFIG_IP6_NF_MANGLE=y +CONFIG_IP6_NF_RAW=y +# CONFIG_IP6_NF_SECURITY is not set +# CONFIG_NF_NAT_IPV6 is not set +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_ULOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +CONFIG_L2TP=y +# CONFIG_L2TP_DEBUGFS is not set +# CONFIG_L2TP_V3 is not set +CONFIG_STP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +# CONFIG_BRIDGE_VLAN_FILTERING is not set +CONFIG_HAVE_NET_DSA=y +CONFIG_VLAN_8021Q=y +# CONFIG_VLAN_8021Q_GVRP is not set +# CONFIG_VLAN_8021Q_MVRP is not set +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +# CONFIG_NET_SCH_CBQ is not set +CONFIG_NET_SCH_HTB=y +# CONFIG_NET_SCH_HFSC is not set +# CONFIG_NET_SCH_PRIO is not set +# CONFIG_NET_SCH_MULTIQ is not set +# CONFIG_NET_SCH_RED is not set +# CONFIG_NET_SCH_SFB is not set +# CONFIG_NET_SCH_SFQ is not set +# CONFIG_NET_SCH_TEQL is not set +# CONFIG_NET_SCH_TBF is not set +# CONFIG_NET_SCH_GRED is not set +# CONFIG_NET_SCH_DSMARK is not set +# CONFIG_NET_SCH_NETEM is not set +# CONFIG_NET_SCH_DRR is not set +# CONFIG_NET_SCH_MQPRIO is not set +# CONFIG_NET_SCH_CHOKE is not set +# CONFIG_NET_SCH_QFQ is not set +# CONFIG_NET_SCH_CODEL is not set +# CONFIG_NET_SCH_FQ_CODEL is not set +# CONFIG_NET_SCH_INGRESS is not set +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +# CONFIG_NET_CLS_BASIC is not set +# CONFIG_NET_CLS_TCINDEX is not set +# CONFIG_NET_CLS_ROUTE4 is not set +# CONFIG_NET_CLS_FW is not set +CONFIG_NET_CLS_U32=y +# CONFIG_CLS_U32_PERF is not set +# CONFIG_CLS_U32_MARK is not set +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +# CONFIG_NET_CLS_FLOW is not set +# CONFIG_NET_CLS_CGROUP is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +# CONFIG_NET_EMATCH_CMP is not set +# CONFIG_NET_EMATCH_NBYTE is not set +CONFIG_NET_EMATCH_U32=y +# CONFIG_NET_EMATCH_META is not set +# CONFIG_NET_EMATCH_TEXT is not set +CONFIG_NET_CLS_ACT=y +# CONFIG_NET_ACT_POLICE is not set +# CONFIG_NET_ACT_GACT is not set +# CONFIG_NET_ACT_MIRRED is not set +# CONFIG_NET_ACT_IPT is not set +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +# CONFIG_NET_ACT_SKBEDIT is not set +# CONFIG_NET_ACT_CSUM is not set +# CONFIG_NET_CLS_IND is not set +CONFIG_NET_SCH_FIFO=y +# CONFIG_DCB is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_OPENVSWITCH is not set +# CONFIG_VSOCKETS is not set +# CONFIG_NETLINK_MMAP is not set +# CONFIG_NETLINK_DIAG is not set +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_NETPRIO_CGROUP is not set +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NET_DROP_MONITOR is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +# CONFIG_BT_RFCOMM_TTY is not set +# CONFIG_BT_BNEP is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +# CONFIG_BT_HCIUART_BCSP is not set +# CONFIG_BT_HCIUART_ATH3K is not set +# CONFIG_BT_HCIUART_LL is not set +# CONFIG_BT_HCIUART_3WIRE is not set +CONFIG_BT_HCIUART_RTKH5=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +# CONFIG_BT_HCIVHCI is not set +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +CONFIG_NL80211_TESTMODE=y +# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +CONFIG_LIB80211=y +CONFIG_LIB80211_CRYPT_WEP=y +CONFIG_LIB80211_CRYPT_CCMP=y +CONFIG_LIB80211_CRYPT_TKIP=y +# CONFIG_LIB80211_DEBUG is not set +# CONFIG_CFG80211_ALLOW_RECONNECT is not set +CONFIG_MAC80211=y +CONFIG_MAC80211_HAS_RC=y +# CONFIG_MAC80211_RC_PID is not set +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +CONFIG_MAC80211_MESH=y +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_SPI=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set + +# +# Default contiguous memory area size: +# +CONFIG_CMA_SIZE_MBYTES=48 +CONFIG_CMA_SIZE_SEL_MBYTES=y +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set +# CONFIG_CMA_SIZE_SEL_MIN is not set +# CONFIG_CMA_SIZE_SEL_MAX is not set +CONFIG_CMA_ALIGNMENT=2 +CONFIG_CMA_AREAS=1 + +# +# Bus devices +# +# CONFIG_ARM_CCI is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +CONFIG_DTC=y +CONFIG_OF=y + +# +# Device Tree and Open Firmware support +# +CONFIG_PROC_DEVICETREE=y +# CONFIG_OF_SELFTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_DEVICE=y +CONFIG_OF_I2C=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_DRBD is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ATMEL_PWM is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ATMEL_SSC is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_BMP085_I2C is not set +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# ACTIONS MISC INFO ACCESS DRIVER +# +CONFIG_MISC_INFO=y + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +# CONFIG_BCACHE is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=y +# CONFIG_DM_DEBUG is not set +CONFIG_DM_BUFIO=y +CONFIG_DM_CRYPT=y +# CONFIG_DM_SNAPSHOT is not set +# CONFIG_DM_THIN_PROVISIONING is not set +# CONFIG_DM_CACHE is not set +# CONFIG_DM_MIRROR is not set +# CONFIG_DM_RAID is not set +# CONFIG_DM_ZERO is not set +# CONFIG_DM_MULTIPATH is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +# CONFIG_DM_FLAKEY is not set +CONFIG_DM_VERITY=y +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_NET_CORE=y +# CONFIG_BONDING is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_MII=y +# CONFIG_IFB is not set +# CONFIG_NET_TEAM is not set +# CONFIG_MACVLAN is not set +# CONFIG_VXLAN is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_VETH is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +CONFIG_ETHERNET=y +CONFIG_NET_CADENCE=y +# CONFIG_ARM_AT91_ETHER is not set +# CONFIG_MACB is not set +CONFIG_NET_VENDOR_BROADCOM=y +# CONFIG_B44 is not set +# CONFIG_NET_CALXEDA_XGMAC is not set +CONFIG_NET_VENDOR_CIRRUS=y +# CONFIG_CS89x0 is not set +# CONFIG_DM9000 is not set +CONFIG_NET_VENDOR_ACTS=m +CONFIG_POLL_PHY_STATE=y +# CONFIG_PHY_REALTEK_RTL8201 is not set +CONFIG_PHY_CORECHIP_SR8201G=y +# CONFIG_NET_VENDOR_SG8201G is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_FARADAY=y +# CONFIG_FTMAC100 is not set +# CONFIG_FTGMAC100 is not set +CONFIG_NET_VENDOR_INTEL=y +CONFIG_NET_VENDOR_I825XX=y +CONFIG_NET_VENDOR_MARVELL=y +# CONFIG_MVMDIO is not set +CONFIG_NET_VENDOR_MICREL=y +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +CONFIG_NET_VENDOR_MICROCHIP=y +# CONFIG_ENC28J60 is not set +CONFIG_NET_VENDOR_NATSEMI=y +CONFIG_NET_VENDOR_8390=y +# CONFIG_AX88796 is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_SEEQ=y +CONFIG_NET_VENDOR_SMSC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +CONFIG_NET_VENDOR_STMICRO=y +# CONFIG_STMMAC_ETH is not set +CONFIG_NET_VENDOR_WIZNET=y +# CONFIG_WIZNET_W5100 is not set +# CONFIG_WIZNET_W5300 is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_AT803X_PHY is not set +# CONFIG_AMD_PHY is not set +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_BCM87XX_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_NATIONAL_PHY is not set +# CONFIG_STE10XP is not set +# CONFIG_LSI_ET1011C_PHY is not set +# CONFIG_MICREL_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MICREL_KS8995MA is not set +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=m +CONFIG_PPPOL2TP=m +CONFIG_PPPOLAC=m +CONFIG_PPPOPNS=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +# CONFIG_SLIP is not set +CONFIG_SLHC=m + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +CONFIG_USB_RTL8150=y +CONFIG_USB_RTL8152=y +CONFIG_USB_USBNET=m +# CONFIG_USB_NET_ASIX is not set +# CONFIG_USB_NET_SR9700 is not set +# CONFIG_USB_NET_SR9800 is not set +# CONFIG_USB_NET_AX8817X is not set +# CONFIG_USB_NET_AX88179_178A is not set +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=m +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +# CONFIG_USB_NET_ZAURUS is not set +# CONFIG_USB_NET_CX82310_ETH is not set +# CONFIG_USB_NET_KALMIA is not set +# CONFIG_USB_NET_QMI_WWAN is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_USB_VL600 is not set +CONFIG_WLAN=y +# CONFIG_LIBERTAS_THINFIRM is not set +# CONFIG_AT76C50X_USB is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +CONFIG_RTL8187=m +CONFIG_RTL8187_LEDS=y +# CONFIG_MAC80211_HWSIM is not set +# CONFIG_WIFI_CONTROL_FUNC is not set +CONFIG_BROADCOM_WIFI_BT=m +CONFIG_RTL8723BS=m +# CONFIG_RTL8723BS_VQ0 is not set +CONFIG_RTL8723BU=m +CONFIG_RTL8188ETV=m +CONFIG_ATH_COMMON=m +CONFIG_ATH_CARDS=m +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH9K_HW=m +CONFIG_ATH9K_COMMON=m +CONFIG_ATH9K_BTCOEX_SUPPORT=y +CONFIG_ATH9K=m +CONFIG_ATH9K_AHB=y +# CONFIG_ATH9K_DEBUGFS is not set +CONFIG_ATH9K_LEGACY_RATE_CONTROL=y +CONFIG_ATH9K_HTC=m +# CONFIG_ATH9K_HTC_DEBUGFS is not set +CONFIG_CARL9170=m +CONFIG_CARL9170_LEDS=y +CONFIG_CARL9170_WPC=y +# CONFIG_CARL9170_HWRNG is not set +# CONFIG_ATH6KL is not set +CONFIG_AR5523=m +# CONFIG_B43 is not set +# CONFIG_B43LEGACY is not set +# CONFIG_BRCMFMAC is not set +CONFIG_HOSTAP=y +CONFIG_HOSTAP_FIRMWARE=y +CONFIG_HOSTAP_FIRMWARE_NVRAM=y +# CONFIG_LIBERTAS is not set +# CONFIG_P54_COMMON is not set +CONFIG_RT2X00=y +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +# CONFIG_RT2800USB_UNKNOWN is not set +CONFIG_RT2800_LIB=m +CONFIG_RT2X00_LIB_USB=m +CONFIG_RT2X00_LIB=m +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_LEDS=y +# CONFIG_RT2X00_DEBUG is not set +CONFIG_RTLWIFI=y +# CONFIG_RTLWIFI_DEBUG is not set +CONFIG_RTL8192CU=m +CONFIG_RTL8192C_COMMON=m +CONFIG_WL_TI=y +CONFIG_WL1251=m +# CONFIG_WL1251_SPI is not set +# CONFIG_WL1251_SDIO is not set +CONFIG_WL12XX=m +CONFIG_WL18XX=m +CONFIG_WLCORE=m +# CONFIG_WLCORE_SPI is not set +# CONFIG_WLCORE_SDIO is not set +# CONFIG_ZD1211RW is not set +# CONFIG_MWIFIEX is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_FF_MEMLESS=y +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_SPARSEKMAP is not set +# CONFIG_INPUT_MATRIXKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_ATC260X_ADCKEY is not set +CONFIG_KEYBOARD_ATC260X_ONOFF=y +CONFIG_KEYBOARD_ATC260X_IRKEY=m +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_JOYSTICK=y +# CONFIG_JOYSTICK_ANALOG is not set +# CONFIG_JOYSTICK_A3D is not set +# CONFIG_JOYSTICK_ADI is not set +# CONFIG_JOYSTICK_COBRA is not set +# CONFIG_JOYSTICK_GF2K is not set +# CONFIG_JOYSTICK_GRIP is not set +# CONFIG_JOYSTICK_GRIP_MP is not set +# CONFIG_JOYSTICK_GUILLEMOT is not set +# CONFIG_JOYSTICK_INTERACT is not set +# CONFIG_JOYSTICK_SIDEWINDER is not set +# CONFIG_JOYSTICK_TMDC is not set +# CONFIG_JOYSTICK_IFORCE is not set +# CONFIG_JOYSTICK_WARRIOR is not set +# CONFIG_JOYSTICK_MAGELLAN is not set +# CONFIG_JOYSTICK_SPACEORB is not set +# CONFIG_JOYSTICK_SPACEBALL is not set +# CONFIG_JOYSTICK_STINGER is not set +# CONFIG_JOYSTICK_TWIDJOY is not set +# CONFIG_JOYSTICK_ZHENHUA is not set +# CONFIG_JOYSTICK_AS5011 is not set +# CONFIG_JOYSTICK_JOYDUMP is not set +CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y +CONFIG_JOYSTICK_XPAD_LEDS=y +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_USB_ACECAD=y +CONFIG_TABLET_USB_AIPTEK=y +CONFIG_TABLET_USB_GTCO=y +CONFIG_TABLET_USB_HANWANG=y +CONFIG_TABLET_USB_KBTAB=y +CONFIG_TABLET_USB_WACOM=y +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +# CONFIG_TOUCHSCREEN_TPS6507X is not set +CONFIG_TOUCHSCREEN_GSLX680=m +# CONFIG_TOUCHSCREEN_GSL1680F is not set +CONFIG_TOUCHSCREEN_GSL3680=m +CONFIG_TOUCHSCREEN_FT5X06=m +# CONFIG_TOUCHSCREEN_GT9XX is not set +CONFIG_TOUCHSCREEN_DETECT=m +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +CONFIG_INPUT_UINPUT=y +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_PWM_BEEPER is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_CMA3000 is not set +CONFIG_INPUT_GSENSOR=y +CONFIG_GSENSOR_DETECT=m +CONFIG_GSENSOR_BMA222=m +# CONFIG_GSENSOR_STK8312 is not set +CONFIG_GSENSOR_STK8313=m +CONFIG_GSENSOR_MC3236=m +# CONFIG_GSENSOR_MC3230 is not set +CONFIG_GSENSOR_MC3232=m +# CONFIG_GSENSOR_MMA7660 is not set +CONFIG_GSENSOR_KIONIX=m +CONFIG_GSENSOR_MIR3DA=m +CONFIG_INPUT_LIGHT_GSENSOR=y +CONFIG_LIGHT_GSENSOR_LTR301=m + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +# CONFIG_DEVKMEM is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_TIMBERDALE is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_TTY_PRINTK is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_HW_RANDOM_ATMEL is not set +# CONFIG_HW_RANDOM_EXYNOS is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +# CONFIG_I2C_MUX is not set +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=m + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_SC18IS602 is not set +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set +CONFIG_SPI_OWL=y + +# +# Qualcomm MSM SSBI bus support +# +# CONFIG_SSBI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +# CONFIG_PPS is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +# CONFIG_PTP_1588_CLOCK is not set + +# +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks. +# +# CONFIG_PTP_1588_CLOCK_PCH is not set +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINCTRL_SINGLE is not set +# CONFIG_PINCTRL_EXYNOS is not set +# CONFIG_PINCTRL_EXYNOS5440 is not set +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIOLIB=y +CONFIG_OF_GPIO=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers: +# +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_RCAR is not set +# CONFIG_GPIO_TS5500 is not set +# CONFIG_GPIO_GRGPIO is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set +CONFIG_GPIO_ATC260X=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_74X164 is not set + +# +# AC97 GPIO expanders: +# + +# +# MODULbus GPIO expanders: +# + +# +# USB GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_GENERIC_ADC_BATTERY is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_BQ27441 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GOLDFISH is not set +CONFIG_CHARGER_DRV_ATC206X=y +# CONFIG_ATC260X_CAP_GAUGE is not set +# CONFIG_POWER_RESET is not set +# CONFIG_POWER_RESET_RESTART is not set +# CONFIG_POWER_AVS is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7310 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_IIO_HWMON is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +CONFIG_SENSORS_ATC260X=y +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +CONFIG_THERMAL_GOV_FAIR_SHARE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_CPU_THERMAL=y +CONFIG_THERMAL_EMULATION=y +CONFIG_OWL_THERMAL=y +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +# CONFIG_BCMA is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_STMPE is not set +# CONFIG_MFD_SYSCON is not set +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +# CONFIG_MFD_WL1273_CORE is not set +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_MFD_ATC260X=y +# CONFIG_VEXPRESS_CONFIG is not set +CONFIG_ATC260X_PWM=y +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +# CONFIG_REGULATOR_DUMMY is not set +# CONFIG_REGULATOR_FIXED_VOLTAGE is not set +# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_GPIO is not set +# CONFIG_REGULATOR_AD5398 is not set +# CONFIG_REGULATOR_FAN53555 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_REGULATOR_ATC260X=y +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CAMERA_SUPPORT=y +# CONFIG_MEDIA_ANALOG_TV_SUPPORT is not set +# CONFIG_MEDIA_DIGITAL_TV_SUPPORT is not set +# CONFIG_MEDIA_RADIO_SUPPORT is not set +# CONFIG_MEDIA_RC_SUPPORT is not set +# CONFIG_MEDIA_CONTROLLER is not set +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +CONFIG_VIDEOBUF_GEN=y +CONFIG_VIDEOBUF_DMA_CONTIG=y +CONFIG_VIDEOBUF2_CORE=y +CONFIG_VIDEOBUF2_MEMOPS=y +CONFIG_VIDEOBUF2_DMA_CONTIG=y +CONFIG_VIDEOBUF2_VMALLOC=m +# CONFIG_VIDEO_V4L2_INT_DEVICE is not set +# CONFIG_TTPCI_EEPROM is not set + +# +# Media drivers +# +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +CONFIG_USB_VIDEO_CLASS=m +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_USB_SN9C102 is not set + +# +# Webcam, TV (analog/digital) USB devices +# +# CONFIG_VIDEO_EM28XX is not set +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_VIDEO_TIMBERDALE is not set +CONFIG_SOC_CAMERA=y +# CONFIG_SOC_CAMERA_PLATFORM is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set +# CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_V4L_MEM2MEM_DRIVERS is not set +# CONFIG_V4L_TEST_DRIVERS is not set + +# +# Supported MMC/SDIO adapters +# +# CONFIG_CYPRESS_FIRMWARE is not set + +# +# Media ancillary drivers (tuners, sensors, i2c, frontends) +# +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y + +# +# Audio decoders, processors and mixers +# + +# +# RDS decoders +# + +# +# Video decoders +# + +# +# Video and audio decoders +# + +# +# Video encoders +# + +# +# Camera sensor devices +# + +# +# Flash devices +# + +# +# Video improvement chips +# + +# +# Miscelaneous helper chips +# + +# +# Sensors used on soc_camera driver +# + +# +# soc_camera sensor drivers +# +# CONFIG_SOC_CAMERA_IMX074 is not set +# CONFIG_SOC_CAMERA_MT9M001 is not set +# CONFIG_SOC_CAMERA_MT9M111 is not set +# CONFIG_SOC_CAMERA_MT9T031 is not set +# CONFIG_SOC_CAMERA_MT9T112 is not set +# CONFIG_SOC_CAMERA_MT9V022 is not set +# CONFIG_SOC_CAMERA_OV2640 is not set +# CONFIG_SOC_CAMERA_OV5642 is not set +# CONFIG_SOC_CAMERA_OV6650 is not set +# CONFIG_SOC_CAMERA_OV772X is not set +# CONFIG_SOC_CAMERA_OV9640 is not set +# CONFIG_SOC_CAMERA_OV9740 is not set +# CONFIG_SOC_CAMERA_RJ54N1 is not set +# CONFIG_SOC_CAMERA_TW9910 is not set +CONFIG_ACTIONS_CAMERA=y + +# +# Sensors used on Actions driver +# +CONFIG_GC2035=m +CONFIG_GC0308=m +CONFIG_GC2155=m +CONFIG_GC0312=m +CONFIG_GC2145=m +CONFIG_GC0328=m +CONFIG_GC0329=m +CONFIG_SP0A19=m +CONFIG_HI257=m +CONFIG_HI708=m +CONFIG_OV5640=m +CONFIG_SP2519=m +CONFIG_OV2686=m +CONFIG_SOC5140=m +CONFIG_SP0718=m +CONFIG_SIV121DU=m +CONFIG_FLASHLIGHT=m +CONFIG_OWL7059_CAMERA=m +CONFIG_SENSOR_DETECT=m + +# +# Tools to develop new frontends +# +# CONFIG_DVB_DUMMY_FE is not set + +# +# Graphics support +# +CONFIG_FB_TFT=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_FB_FLEX=m +CONFIG_FB_TFT_FBTFT_DEVICE=m +CONFIG_DRM=m +# CONFIG_DRM_UDL is not set +# CONFIG_DRM_TILCDC is not set +CONFIG_IMG_PVR_GPU=y +CONFIG_IMG_SGX_5xx=m +# CONFIG_IMG_ROGUE_T6xxx is not set +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_HDMI=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +CONFIG_FB_SYS_FILLRECT=m +CONFIG_FB_SYS_COPYAREA=m +CONFIG_FB_SYS_IMAGEBLIT=m +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=m +CONFIG_FB_DEFERRED_IO=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +CONFIG_FB_BACKLIGHT=y +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_ARMHDLCD is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_TMIO is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_GOLDFISH is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_EXYNOS_VIDEO is not set +CONFIG_VIDEO_OWL=y +CONFIG_VIDEO_ACTIONS_VCE=y +CONFIG_VIDEO_ACTIONS_VDE=y +CONFIG_VIDEO_OWL_PWM_BACKLIGHT=y +CONFIG_VIDEO_OWL_DSS=y +CONFIG_VIDEO_OWL_DE_ATM7059=y +# CONFIG_VIDEO_OWL_DE_ATM9009 is not set +CONFIG_VIDEO_OWL_MMU_SUPPORT=y +CONFIG_VIDEO_OWL_MMU_ION_SUPPORT=y +CONFIG_VIDEO_OWL_LCDC=y +CONFIG_VIDEO_OWL_DSI=y +CONFIG_VIDEO_OWL_NO_DVI=y +CONFIG_VIDEO_OWL_HDMI=y +CONFIG_FB_COLOR_MODE_XRGB32=y +CONFIG_FB_MAP_TO_DE=y +CONFIG_FB_DEFAULT_MIRROR_TO_HDMI=y +CONFIG_VIDEO_OWL_FB=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +# CONFIG_BACKLIGHT_PWM is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LM3630 is not set +# CONFIG_BACKLIGHT_LM3639 is not set +# CONFIG_BACKLIGHT_LP855X is not set +# CONFIG_ADF is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_LOGO_ACTIONS_CLUT224 is not set +# CONFIG_FB_SSD1307 is not set +CONFIG_SOUND=y +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_RAWMIDI=y +CONFIG_SND_COMPRESS_OFFLOAD=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_6FIRE is not set +CONFIG_SND_SOC=y +CONFIG_SND_SOC_OWL=y +CONFIG_SND_SOC_ATC2603A=y +CONFIG_SND_SOC_ATC2603C=y +CONFIG_SND_SOC_HDMI_OWL=y +CONFIG_SND_SOC_DAI_OWL=y +CONFIG_SND_SOC_ALL_PMU_OWL=y +# CONFIG_SND_ATMEL_SOC is not set +# CONFIG_SND_DESIGNWARE_I2S is not set +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +# CONFIG_SND_SIMPLE_CARD is not set +# CONFIG_SOUND_PRIME is not set + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +CONFIG_HIDRAW=y +CONFIG_UHID=y +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=y +CONFIG_HID_ACRUX=y +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=y +# CONFIG_HID_APPLEIR is not set +# CONFIG_HID_AUREAL is not set +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_PRODIKEYS=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_DRAGONRISE=y +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=y +CONFIG_HID_ELECOM=y +CONFIG_HID_EZKEY=y +CONFIG_HID_HOLTEK=y +# CONFIG_HOLTEK_FF is not set +CONFIG_HID_KEYTOUCH=y +CONFIG_HID_KYE=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_WALTOP=y +CONFIG_HID_GYRATION=y +# CONFIG_HID_ICADE is not set +CONFIG_HID_TWINHAN=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LCPOWER=y +# CONFIG_HID_LENOVO_TPKBD is not set +CONFIG_HID_LOGITECH=y +CONFIG_HID_LOGITECH_DJ=y +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +CONFIG_HID_MULTITOUCH=y +CONFIG_HID_NTRIG=y +CONFIG_HID_ORTEK=y +CONFIG_HID_PANTHERLORD=y +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PETALYNX=y +CONFIG_HID_PICOLCD=y +# CONFIG_HID_PICOLCD_FB is not set +# CONFIG_HID_PICOLCD_BACKLIGHT is not set +# CONFIG_HID_PICOLCD_LEDS is not set +CONFIG_HID_PRIMAX=y +# CONFIG_HID_PS3REMOTE is not set +CONFIG_HID_ROCCAT=y +CONFIG_HID_SAITEK=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SPEEDLINK=y +# CONFIG_HID_STEELSERIES is not set +CONFIG_HID_SUNPLUS=y +CONFIG_HID_GREENASIA=y +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=y +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=y +CONFIG_HID_TOPSEED=y +# CONFIG_HID_THINGM is not set +CONFIG_HID_THRUSTMASTER=y +# CONFIG_THRUSTMASTER_FF is not set +CONFIG_HID_WACOM=y +CONFIG_HID_WIIMOTE=y +CONFIG_HID_WIIMOTE_EXT=y +CONFIG_HID_ZEROPLUS=y +# CONFIG_ZEROPLUS_FF is not set +CONFIG_HID_ZYDACRON=y +# CONFIG_HID_SENSOR_HUB is not set + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +CONFIG_USB_HIDDEV=y + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB_ARCH_HAS_XHCI=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +CONFIG_USB_DEBUG=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +CONFIG_USB_ACTIONS_MON=y +CONFIG_USB_PLATFORM_LINUX=y +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +# CONFIG_USB_XHCI_HCD_DEBUGGING is not set +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_AOTG_HCD=y +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_RENESAS_USBHS is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=y +# CONFIG_USB_PRINTER is not set +CONFIG_USB_WDM=m +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +CONFIG_USB_DWC3=y +# CONFIG_USB_DWC3_HOST is not set +# CONFIG_USB_DWC3_GADGET is not set +CONFIG_USB_DWC3_DUAL_ROLE=y +# CONFIG_USB_DWC3_DEBUG is not set +# CONFIG_USB_CHIPIDEA is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CONSOLE=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_FUNSOFT=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F81232=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +# CONFIG_USB_SERIAL_KEYSPAN is not set +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_METRO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MOTOROLA=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_HP4X=m +CONFIG_USB_SERIAL_SAFE=m +CONFIG_USB_SERIAL_SAFE_PADDED=y +CONFIG_USB_SERIAL_SIEMENS_MPI=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_WWAN=y +CONFIG_USB_SERIAL_OPTION=y +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_ZIO=m +CONFIG_USB_SERIAL_WISHBONE=m +CONFIG_USB_SERIAL_ZTE=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_EZUSB_FX2=m +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_PHY is not set +# CONFIG_USB_OTG_WAKELOCK is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2 + +# +# USB Peripheral Controller +# +# CONFIG_USB_FUSB300 is not set +# CONFIG_USB_R8A66597 is not set +# CONFIG_USB_PXA27X is not set +# CONFIG_USB_MV_UDC is not set +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_M66592 is not set +# CONFIG_USB_NET2272 is not set +# CONFIG_USB_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +CONFIG_USB_LIBCOMPOSITE=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_G_NCM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FUNCTIONFS is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_G_ANDROID is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_ACM_MS is not set +# CONFIG_USB_G_MULTI is not set +# CONFIG_USB_G_HID is not set +# CONFIG_USB_G_DBGP is not set +# CONFIG_USB_G_WEBCAM is not set +CONFIG_USB_ACTIONS_ADFUSERVER=y +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_CLKGATE is not set +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_TSD_MMC_BLOCK is not set +# CONFIG_CARD_TO_CARD_MMC_BLOCK is not set +CONFIG_MMC_BLOCK_MINORS=16 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_MMC_BLOCK_DEFERRED_RESUME is not set +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_SDHCI_PXAV3 is not set +# CONFIG_MMC_SDHCI_PXAV2 is not set +CONFIG_MMC_OWL=y +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA9633 is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_PWM is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_RENESAS_TPU is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_OT200 is not set +# CONFIG_LEDS_BLINKM is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +CONFIG_LEDS_TRIGGER_CPU=y +# CONFIG_LEDS_TRIGGER_GPIO is not set +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_LEDS_TRIGGER_CAMERA is not set +CONFIG_SWITCH=y +CONFIG_SWITCH_GPIO=y +# CONFIG_ACCESSIBILITY is not set +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +# CONFIG_RTC_DRV_DS2404 is not set +CONFIG_RTC_DRV_ATC206X=y + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_SNVS is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +CONFIG_DMADEVICES=y +# CONFIG_DMADEVICES_DEBUG is not set + +# +# DMA Devices +# +CONFIG_OWL_DMAC=y +# CONFIG_DW_DMAC is not set +# CONFIG_TIMB_DMA is not set +CONFIG_DMA_ENGINE=y +CONFIG_DMA_OF=y + +# +# DMA Clients +# +# CONFIG_ASYNC_TX_DMA is not set +# CONFIG_DMATEST is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_USBIP_CORE is not set +# CONFIG_W35UND is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_RTLLIB is not set +# CONFIG_R8712U is not set +# CONFIG_RTS5139 is not set +# CONFIG_TRANZPORT is not set +# CONFIG_LINE6_USB is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set +# CONFIG_VT6656 is not set + +# +# IIO staging drivers +# + +# +# Accelerometers +# +# CONFIG_ADIS16201 is not set +# CONFIG_ADIS16203 is not set +# CONFIG_ADIS16204 is not set +# CONFIG_ADIS16209 is not set +# CONFIG_ADIS16220 is not set +# CONFIG_ADIS16240 is not set +# CONFIG_LIS3L02DQ is not set +# CONFIG_SCA3000 is not set + +# +# Analog to digital converters +# +# CONFIG_AD7291 is not set +# CONFIG_AD7606 is not set +# CONFIG_AD799X is not set +# CONFIG_AD7780 is not set +# CONFIG_AD7816 is not set +# CONFIG_AD7192 is not set +# CONFIG_AD7280 is not set + +# +# Analog digital bi-direction converters +# +# CONFIG_ADT7316 is not set + +# +# Capacitance to digital converters +# +# CONFIG_AD7150 is not set +# CONFIG_AD7152 is not set +# CONFIG_AD7746 is not set + +# +# Direct Digital Synthesis +# +# CONFIG_AD5930 is not set +# CONFIG_AD9832 is not set +# CONFIG_AD9834 is not set +# CONFIG_AD9850 is not set +# CONFIG_AD9852 is not set +# CONFIG_AD9910 is not set +# CONFIG_AD9951 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16060 is not set +# CONFIG_ADIS16130 is not set +# CONFIG_ADIS16260 is not set + +# +# Network Analyzer, Impedance Converters +# +# CONFIG_AD5933 is not set + +# +# Light sensors +# +# CONFIG_SENSORS_ISL29018 is not set +# CONFIG_SENSORS_ISL29028 is not set +# CONFIG_TSL2583 is not set +# CONFIG_TSL2x7x is not set + +# +# Magnetometer sensors +# +# CONFIG_SENSORS_HMC5843 is not set + +# +# Active energy metering IC +# +# CONFIG_ADE7753 is not set +# CONFIG_ADE7754 is not set +# CONFIG_ADE7758 is not set +# CONFIG_ADE7759 is not set +# CONFIG_ADE7854 is not set + +# +# Resolver to digital converters +# +# CONFIG_AD2S90 is not set +# CONFIG_AD2S1200 is not set +# CONFIG_AD2S1210 is not set + +# +# Triggers - standalone +# +# CONFIG_IIO_PERIODIC_RTC_TRIGGER is not set +# CONFIG_IIO_GPIO_TRIGGER is not set +# CONFIG_IIO_SYSFS_TRIGGER is not set +# CONFIG_IIO_SIMPLE_DUMMY is not set +CONFIG_ZSMALLOC=y +CONFIG_ZRAM=y +# CONFIG_ZRAM_DEBUG is not set +# CONFIG_USB_ENESTORAGE is not set +# CONFIG_BCM_WIMAX is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +# CONFIG_STAGING_MEDIA is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_BINDER_IPC_32BIT=y +CONFIG_ASHMEM=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_TIMED_OUTPUT=y +CONFIG_ANDROID_TIMED_GPIO=y +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y +# CONFIG_ANDROID_SWITCH is not set +CONFIG_ANDROID_INTF_ALARM_DEV=y +CONFIG_SYNC=y +CONFIG_SW_SYNC=y +CONFIG_SW_SYNC_USER=y +CONFIG_ION=y +# CONFIG_ION_TEST is not set +# CONFIG_ION_DUMMY is not set +CONFIG_ION_OWL=y +# CONFIG_USB_WPAN_HCD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_CSR_WIFI is not set +# CONFIG_CED1401 is not set +# CONFIG_DGRP is not set +# CONFIG_USB_DWC2 is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_DEBUG is not set +# CONFIG_COMMON_CLK_SI5351 is not set + +# +# Hardware Spinlock drivers +# +CONFIG_CLKSRC_OF=y +# CONFIG_MAILBOX is not set +CONFIG_IOMMU_SUPPORT=y +CONFIG_OF_IOMMU=y + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# +# CONFIG_PM_DEVFREQ is not set +# CONFIG_EXTCON is not set +# CONFIG_MEMORY is not set +CONFIG_IIO=y +CONFIG_IIO_BUFFER=y +# CONFIG_IIO_BUFFER_CB is not set +CONFIG_IIO_KFIFO_BUF=y +CONFIG_IIO_TRIGGER=y +CONFIG_IIO_CONSUMERS_PER_TRIGGER=2 + +# +# Accelerometers +# +# CONFIG_KXSD9 is not set +# CONFIG_IIO_ST_ACCEL_3AXIS is not set + +# +# Analog to digital converters +# +# CONFIG_AD7266 is not set +# CONFIG_AD7298 is not set +# CONFIG_AD7923 is not set +# CONFIG_AD7791 is not set +# CONFIG_AD7793 is not set +# CONFIG_AD7476 is not set +# CONFIG_AD7887 is not set +# CONFIG_EXYNOS_ADC is not set +# CONFIG_MAX1363 is not set +# CONFIG_TI_ADC081C is not set + +# +# Amplifiers +# +# CONFIG_AD8366 is not set + +# +# Hid Sensor IIO Common +# + +# +# Digital to analog converters +# +# CONFIG_AD5064 is not set +# CONFIG_AD5360 is not set +# CONFIG_AD5380 is not set +# CONFIG_AD5421 is not set +# CONFIG_AD5624R_SPI is not set +# CONFIG_AD5446 is not set +# CONFIG_AD5449 is not set +# CONFIG_AD5504 is not set +# CONFIG_AD5755 is not set +# CONFIG_AD5764 is not set +# CONFIG_AD5791 is not set +# CONFIG_AD5686 is not set +# CONFIG_MAX517 is not set +# CONFIG_MCP4725 is not set + +# +# Frequency Synthesizers DDS/PLL +# + +# +# Clock Generator/Distribution +# +# CONFIG_AD9523 is not set + +# +# Phase-Locked Loop (PLL) frequency synthesizers +# +# CONFIG_ADF4350 is not set + +# +# Digital gyroscope sensors +# +# CONFIG_ADIS16080 is not set +# CONFIG_ADIS16136 is not set +# CONFIG_ADXRS450 is not set +# CONFIG_IIO_ST_GYRO_3AXIS is not set +# CONFIG_ITG3200 is not set +CONFIG_IIO_INV_MPU6515=m + +# +# Inertial measurement units +# +# CONFIG_ADIS16400 is not set +# CONFIG_ADIS16480 is not set +# CONFIG_INV_MPU6050_IIO is not set + +# +# Light sensors +# +# CONFIG_ADJD_S311 is not set +# CONFIG_SENSORS_TSL2563 is not set +# CONFIG_VCNL4000 is not set + +# +# Magnetometer sensors +# +# CONFIG_AK8975 is not set +# CONFIG_IIO_ST_MAGN_3AXIS is not set +CONFIG_PWM=y +CONFIG_PWM_OWL=y +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +# CONFIG_IPACK_BUS is not set +# CONFIG_RESET_CONTROLLER is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_DEFAULTS_TO_ORDERED=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_EXT4_FS=y +# CONFIG_EXT4_FS_POSIX_ACL is not set +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_FANOTIFY is not set +# CONFIG_QUOTA is not set +# CONFIG_QUOTACTL is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=y +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_CONFIGFS_FS=y +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_PSTORE=y +CONFIG_PSTORE_CONSOLE=y +CONFIG_PSTORE_RAM=y +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_F2FS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_SWAP is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_XATTR is not set +CONFIG_CIFS_DEBUG=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_SMB2 is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +CONFIG_NLS_CODEPAGE_936=y +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set +# CONFIG_DEBUG_READDUMP is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHEDSTATS=y +CONFIG_TIMER_STATS=y +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK=y +CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=800 +# CONFIG_DEBUG_KMEMLEAK_TEST is not set +CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_DEBUG_PREEMPT=y +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +CONFIG_STACKTRACE=y +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_HIGHMEM is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU_DELAY is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +CONFIG_RCU_CPU_STALL_VERBOSE=y +# CONFIG_RCU_CPU_STALL_INFO is not set +# CONFIG_RCU_TRACE is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_LKDTM is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_NOP_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACE_CLOCK=y +CONFIG_RING_BUFFER=y +CONFIG_EVENT_TRACING=y +CONFIG_CONTEXT_SWITCH_TRACER=y +CONFIG_TRACING=y +CONFIG_GENERIC_TRACER=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_PREEMPT_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_FTRACE_SYSCALLS is not set +# CONFIG_TRACER_SNAPSHOT is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +CONFIG_BLK_DEV_IO_TRACE=y +# CONFIG_PROBE_EVENTS is not set +# CONFIG_FTRACE_STARTUP_TEST is not set +# CONFIG_RING_BUFFER_BENCHMARK is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +CONFIG_DYNAMIC_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_RODATA is not set +CONFIG_DEBUG_LL=y +CONFIG_DEBUG_LL_UART_NONE=y +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" +CONFIG_UNCOMPRESS_INCLUDE="mach/uncompress.h" +CONFIG_EARLY_PRINTK=y +# CONFIG_PID_IN_CONTEXTIDR is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY_DMESG_RESTRICT is not set +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_SECURITY_NETWORK=y +# CONFIG_SECURITY_NETWORK_XFRM is not set +CONFIG_SECURITY_PATH=y +CONFIG_LSM_MMAP_MIN_ADDR=32768 +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 +# CONFIG_SECURITY_SELINUX_DISABLE is not set +CONFIG_SECURITY_SELINUX_DEVELOP=y +CONFIG_SECURITY_SELINUX_AVC_STATS=y +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 +# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 +# CONFIG_SECURITY_YAMA is not set +# CONFIG_IMA is not set +CONFIG_DEFAULT_SECURITY_SELINUX=y +# CONFIG_DEFAULT_SECURITY_APPARMOR is not set +# CONFIG_DEFAULT_SECURITY_DAC is not set +CONFIG_DEFAULT_SECURITY="selinux" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_CMAC is not set +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_MICHAEL_MIC=y +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_TWOFISH=y +CONFIG_CRYPTO_TWOFISH_COMMON=y + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +# CONFIG_CRYPTO_LZO is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_HW=y +CONFIG_BINARY_PRINTF=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=m +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +CONFIG_CRC7=m +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +CONFIG_AUDIT_GENERIC=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_REED_SOLOMON=y +CONFIG_REED_SOLOMON_ENC8=y +CONFIG_REED_SOLOMON_DEC8=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=y +CONFIG_TEXTSEARCH_BM=y +CONFIG_TEXTSEARCH_FSM=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_AVERAGE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +# CONFIG_VIRTUALIZATION is not set diff --git a/config/linux-sunxi-dac.config b/config/linux-sunxi-dac.config new file mode 100644 index 000000000..c6ff66cfb --- /dev/null +++ b/config/linux-sunxi-dac.config @@ -0,0 +1,4810 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/arm 4.3.0-rc5 Kernel Configuration +# +CONFIG_ARM=y +CONFIG_ARM_HAS_SG_CHAIN=y +CONFIG_MIGHT_HAVE_PCI=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_FIX_EARLYCON_MEM=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_ARCH_SUPPORTS_UPROBES=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_ARM_PATCH_PHYS_VIRT=y +CONFIG_GENERIC_BUG=y +CONFIG_PGTABLE_LEVELS=2 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_CROSS_COMPILE="" +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_XZ is not set +# CONFIG_KERNEL_LZO is not set +# CONFIG_KERNEL_LZ4 is not set +CONFIG_DEFAULT_HOSTNAME="(none)" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y +CONFIG_CROSS_MEMORY_ATTACH=y +CONFIG_FHANDLE=y +CONFIG_USELIB=y +# CONFIG_AUDIT is not set +CONFIG_HAVE_ARCH_AUDITSYSCALL=y + +# +# IRQ subsystem +# +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_IRQ_SHOW_LEVEL=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_DOMAIN_HIERARCHY=y +CONFIG_HANDLE_DOMAIN_IRQ=y +# CONFIG_IRQ_DOMAIN_DEBUG is not set +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_SPARSE_IRQ=y +CONFIG_GENERIC_TIME_VSYSCALL=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_ARCH_HAS_TICK_BROADCAST=y +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y + +# +# Timers subsystem +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ_COMMON=y +# CONFIG_HZ_PERIODIC is not set +CONFIG_NO_HZ_IDLE=y +# CONFIG_NO_HZ_FULL is not set +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y + +# +# CPU/Task time and stats accounting +# +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set +# CONFIG_IRQ_TIME_ACCOUNTING is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_RCU_EXPERT is not set +CONFIG_SRCU=y +# CONFIG_TASKS_RCU is not set +CONFIG_RCU_STALL_COMMON=y +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_RCU_EXPEDITE_BOOT is not set +CONFIG_BUILD_BIN2C=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 +CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_CGROUPS=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_FREEZER=y +# CONFIG_CGROUP_PIDS is not set +CONFIG_CGROUP_DEVICE=y +CONFIG_CPUSETS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_PAGE_COUNTER=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_SWAP_ENABLED=y +CONFIG_MEMCG_KMEM=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_RT_GROUP_SCHED=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_CGROUP_WRITEBACK=y +# CONFIG_CHECKPOINT_RESTORE is not set +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +# CONFIG_SCHED_AUTOGROUP is not set +# CONFIG_SYSFS_DEPRECATED is not set +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_RD_GZIP=y +CONFIG_RD_BZIP2=y +CONFIG_RD_LZMA=y +CONFIG_RD_XZ=y +CONFIG_RD_LZO=y +CONFIG_RD_LZ4=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_HAVE_UID16=y +CONFIG_BPF=y +CONFIG_EXPERT=y +CONFIG_UID16=y +CONFIG_MULTIUSER=y +# CONFIG_SGETMASK_SYSCALL is not set +CONFIG_SYSFS_SYSCALL=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +# CONFIG_BPF_SYSCALL is not set +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ADVISE_SYSCALLS=y +# CONFIG_USERFAULTFD is not set +CONFIG_MEMBARRIER=y +# CONFIG_EMBEDDED is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +CONFIG_COMPAT_BRK=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_SLUB_CPU_PARTIAL=y +# CONFIG_SYSTEM_DATA_VERIFICATION is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +# CONFIG_JUMP_LABEL is not set +# CONFIG_UPROBES is not set +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y +CONFIG_ARCH_USE_BUILTIN_BSWAP=y +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_OPTPROBES=y +CONFIG_HAVE_ARCH_TRACEHOOK=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DMA_CONTIGUOUS=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GENERIC_IDLE_POLL_SETUP=y +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_HW_BREAKPOINT=y +CONFIG_HAVE_PERF_REGS=y +CONFIG_HAVE_PERF_USER_STACK_DUMP=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y +CONFIG_HAVE_CC_STACKPROTECTOR=y +# CONFIG_CC_STACKPROTECTOR is not set +CONFIG_CC_STACKPROTECTOR_NONE=y +# CONFIG_CC_STACKPROTECTOR_REGULAR is not set +# CONFIG_CC_STACKPROTECTOR_STRONG is not set +CONFIG_HAVE_CONTEXT_TRACKING=y +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_MODULES_USE_ELF_REL=y +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_OLD_SIGSUSPEND3=y +CONFIG_OLD_SIGACTION=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +# CONFIG_MODULE_COMPRESS is not set +CONFIG_MODULES_TREE_LOOKUP=y +CONFIG_STOP_MACHINE=y +CONFIG_BLOCK=y +CONFIG_LBDAF=y +CONFIG_BLK_DEV_BSG=y +CONFIG_BLK_DEV_BSGLIB=y +# CONFIG_BLK_DEV_INTEGRITY is not set +CONFIG_BLK_DEV_THROTTLING=y +# CONFIG_BLK_CMDLINE_PARSER is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CFQ_GROUP_IOSCHED=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +CONFIG_INLINE_READ_UNLOCK=y +CONFIG_INLINE_READ_UNLOCK_IRQ=y +CONFIG_INLINE_WRITE_UNLOCK=y +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y +CONFIG_MUTEX_SPIN_ON_OWNER=y +CONFIG_RWSEM_SPIN_ON_OWNER=y +CONFIG_LOCK_SPIN_ON_OWNER=y +CONFIG_FREEZER=y + +# +# System Type +# +CONFIG_MMU=y +CONFIG_ARCH_MULTIPLATFORM=y +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_MMP is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_LPC32XX is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_SHMOBILE_LEGACY is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C24XX is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP1 is not set + +# +# Multiple platform selection +# + +# +# CPU Core family selection +# +# CONFIG_ARCH_MULTI_V6 is not set +CONFIG_ARCH_MULTI_V7=y +CONFIG_ARCH_MULTI_V6_V7=y +# CONFIG_ARCH_MULTI_CPU_AUTO is not set +# CONFIG_ARCH_VIRT is not set +# CONFIG_ARCH_MVEBU is not set +# CONFIG_ARCH_ALPINE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCM is not set +# CONFIG_ARCH_BERLIN is not set +# CONFIG_ARCH_DIGICOLOR is not set +# CONFIG_ARCH_HIGHBANK is not set +# CONFIG_ARCH_HISI is not set +# CONFIG_ARCH_KEYSTONE is not set +# CONFIG_ARCH_MESON is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_MEDIATEK is not set + +# +# TI OMAP/AM/DM/DRA Family +# +# CONFIG_ARCH_OMAP3 is not set +# CONFIG_ARCH_OMAP4 is not set +# CONFIG_SOC_OMAP5 is not set +# CONFIG_SOC_AM33XX is not set +# CONFIG_SOC_AM43XX is not set +# CONFIG_SOC_DRA7XX is not set +# CONFIG_ARCH_QCOM is not set +# CONFIG_ARCH_ROCKCHIP is not set +# CONFIG_ARCH_SOCFPGA is not set +# CONFIG_PLAT_SPEAR is not set +# CONFIG_ARCH_STI is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_EXYNOS is not set +# CONFIG_ARCH_SHMOBILE_MULTI is not set +CONFIG_ARCH_SUNXI=y +CONFIG_MACH_SUN4I=y +CONFIG_MACH_SUN5I=y +CONFIG_MACH_SUN6I=y +CONFIG_MACH_SUN7I=y +CONFIG_MACH_SUN8I=y +CONFIG_MACH_SUN9I=y +# CONFIG_ARCH_SIRF is not set +# CONFIG_ARCH_TEGRA is not set +# CONFIG_ARCH_UNIPHIER is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_VEXPRESS is not set +# CONFIG_ARCH_WM8850 is not set +# CONFIG_ARCH_ZX is not set +# CONFIG_ARCH_ZYNQ is not set + +# +# Processor Type +# +CONFIG_CPU_V7=y +CONFIG_CPU_32v6K=y +CONFIG_CPU_32v7=y +CONFIG_CPU_ABRT_EV7=y +CONFIG_CPU_PABRT_V7=y +CONFIG_CPU_CACHE_V7=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V7=y +CONFIG_CPU_HAS_ASID=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +# CONFIG_ARM_LPAE is not set +# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set +CONFIG_ARM_THUMB=y +# CONFIG_ARM_THUMBEE is not set +CONFIG_ARM_VIRT_EXT=y +CONFIG_SWP_EMULATE=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_KUSER_HELPERS=y +CONFIG_VDSO=y +CONFIG_OUTER_CACHE=y +CONFIG_OUTER_CACHE_SYNC=y +CONFIG_MIGHT_HAVE_CACHE_L2X0=y +CONFIG_CACHE_L2X0=y +# CONFIG_PL310_ERRATA_588369 is not set +# CONFIG_PL310_ERRATA_727915 is not set +# CONFIG_PL310_ERRATA_753970 is not set +# CONFIG_PL310_ERRATA_769419 is not set +CONFIG_ARM_L1_CACHE_SHIFT_6=y +CONFIG_ARM_L1_CACHE_SHIFT=6 +CONFIG_ARM_DMA_MEM_BUFFERABLE=y +CONFIG_ARM_HEAVY_MB=y +# CONFIG_ARM_KERNMEM_PERMS is not set +CONFIG_MULTI_IRQ_HANDLER=y +# CONFIG_ARM_ERRATA_430973 is not set +CONFIG_ARM_ERRATA_643719=y +# CONFIG_ARM_ERRATA_720789 is not set +# CONFIG_ARM_ERRATA_754322 is not set +# CONFIG_ARM_ERRATA_754327 is not set +# CONFIG_ARM_ERRATA_764369 is not set +# CONFIG_ARM_ERRATA_775420 is not set +# CONFIG_ARM_ERRATA_798181 is not set +# CONFIG_ARM_ERRATA_773022 is not set + +# +# Bus support +# +# CONFIG_PCI is not set +# CONFIG_PCI_DOMAINS_GENERIC is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_HAVE_SMP=y +CONFIG_SMP=y +CONFIG_SMP_ON_UP=y +CONFIG_ARM_CPU_TOPOLOGY=y +CONFIG_SCHED_MC=y +CONFIG_SCHED_SMT=y +CONFIG_HAVE_ARM_ARCH_TIMER=y +# CONFIG_MCPM is not set +# CONFIG_BIG_LITTLE is not set +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_NR_CPUS=4 +CONFIG_HOTPLUG_CPU=y +CONFIG_ARM_PSCI=y +CONFIG_ARCH_NR_GPIO=416 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ_FIXED=0 +CONFIG_HZ_100=y +# CONFIG_HZ_200 is not set +# CONFIG_HZ_250 is not set +# CONFIG_HZ_300 is not set +# CONFIG_HZ_500 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=100 +CONFIG_SCHED_HRTICK=y +# CONFIG_THUMB2_KERNEL is not set +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_HAVE_ARCH_PFN_VALID=y +CONFIG_HIGHMEM=y +CONFIG_HIGHPTE=y +CONFIG_CPU_SW_DOMAIN_PAN=y +CONFIG_HW_PERF_EVENTS=y +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y +# CONFIG_ARM_MODULE_PLTS is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_NO_BOOTMEM=y +CONFIG_MEMORY_ISOLATION=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +CONFIG_COMPACTION=y +CONFIG_MIGRATION=y +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_BOUNCE=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_CLEANCACHE=y +CONFIG_FRONTSWAP=y +CONFIG_CMA=y +# CONFIG_CMA_DEBUG is not set +# CONFIG_CMA_DEBUGFS is not set +CONFIG_CMA_AREAS=7 +CONFIG_ZSWAP=y +CONFIG_ZPOOL=y +# CONFIG_ZBUD is not set +CONFIG_ZSMALLOC=y +# CONFIG_PGTABLE_MAPPING is not set +# CONFIG_ZSMALLOC_STAT is not set +# CONFIG_IDLE_PAGE_TRACKING is not set +CONFIG_FRAME_VECTOR=y +CONFIG_FORCE_MAX_ZONEORDER=11 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set +# CONFIG_SECCOMP is not set +CONFIG_SWIOTLB=y +CONFIG_IOMMU_HELPER=y +# CONFIG_XEN is not set + +# +# Boot options +# +CONFIG_USE_OF=y +CONFIG_ATAGS=y +# CONFIG_DEPRECATED_PARAM_STRUCT is not set +CONFIG_ZBOOT_ROM_TEXT=0 +CONFIG_ZBOOT_ROM_BSS=0 +CONFIG_ARM_APPENDED_DTB=y +CONFIG_ARM_ATAG_DTB_COMPAT=y +CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y +# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set +CONFIG_CMDLINE="" +# CONFIG_KEXEC is not set +# CONFIG_CRASH_DUMP is not set +CONFIG_AUTO_ZRELADDR=y + +# +# CPU Power Management +# + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_GOV_COMMON=y +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=y +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=y +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y + +# +# CPU frequency scaling drivers +# +CONFIG_CPUFREQ_DT=y +CONFIG_ARM_BIG_LITTLE_CPUFREQ=m +CONFIG_ARM_DT_BL_CPUFREQ=m +# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +CONFIG_QORIQ_CPUFREQ=m + +# +# CPU Idle +# +CONFIG_CPU_IDLE=y +CONFIG_CPU_IDLE_GOV_LADDER=y +CONFIG_CPU_IDLE_GOV_MENU=y +CONFIG_DT_IDLE_STATES=y + +# +# ARM CPU Idle Drivers +# +CONFIG_ARM_CPUIDLE=y +# CONFIG_ARM_HIGHBANK_CPUIDLE is not set +# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_VFP=y +CONFIG_VFPv3=y +CONFIG_NEON=y +# CONFIG_KERNEL_MODE_NEON is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_BINFMT_SCRIPT=y +# CONFIG_HAVE_AOUT is not set +# CONFIG_BINFMT_MISC is not set +CONFIG_COREDUMP=y + +# +# Power management options +# +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +# CONFIG_SUSPEND_SKIP_SYNC is not set +# CONFIG_HIBERNATION is not set +CONFIG_PM_SLEEP=y +CONFIG_PM_SLEEP_SMP=y +# CONFIG_PM_AUTOSLEEP is not set +# CONFIG_PM_WAKELOCKS is not set +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_APM_EMULATION is not set +CONFIG_PM_OPP=y +CONFIG_PM_CLK=y +# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set +CONFIG_CPU_PM=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARM_CPU_SUSPEND=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_NET=y +CONFIG_NET_INGRESS=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_DIAG=m +CONFIG_UNIX=y +CONFIG_UNIX_DIAG=m +CONFIG_XFRM=y +CONFIG_XFRM_ALGO=y +CONFIG_XFRM_USER=y +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +CONFIG_XFRM_IPCOMP=y +CONFIG_NET_KEY=y +# CONFIG_NET_KEY_MIGRATE is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_ROUTE_CLASSID=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=y +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IP_TUNNEL=y +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_SYN_COOKIES=y +# CONFIG_NET_IPVTI is not set +CONFIG_NET_UDP_TUNNEL=m +# CONFIG_NET_FOU is not set +# CONFIG_NET_FOU_IP_TUNNELS is not set +CONFIG_INET_AH=y +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y +CONFIG_INET_TUNNEL=y +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_LRO=y +CONFIG_INET_DIAG=m +CONFIG_INET_TCP_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_CONG_BIC=y +CONFIG_TCP_CONG_CUBIC=y +CONFIG_TCP_CONG_WESTWOOD=y +CONFIG_TCP_CONG_HTCP=y +CONFIG_TCP_CONG_HSTCP=y +CONFIG_TCP_CONG_HYBLA=y +CONFIG_TCP_CONG_VEGAS=y +CONFIG_TCP_CONG_SCALABLE=y +CONFIG_TCP_CONG_LP=y +CONFIG_TCP_CONG_VENO=y +CONFIG_TCP_CONG_YEAH=y +CONFIG_TCP_CONG_ILLINOIS=y +# CONFIG_TCP_CONG_DCTCP is not set +# CONFIG_TCP_CONG_CDG is not set +# CONFIG_DEFAULT_BIC is not set +CONFIG_DEFAULT_CUBIC=y +# CONFIG_DEFAULT_HTCP is not set +# CONFIG_DEFAULT_HYBLA is not set +# CONFIG_DEFAULT_VEGAS is not set +# CONFIG_DEFAULT_VENO is not set +# CONFIG_DEFAULT_WESTWOOD is not set +# CONFIG_DEFAULT_RENO is not set +CONFIG_DEFAULT_TCP_CONG="cubic" +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MIP6=m +CONFIG_IPV6_ILA=m +CONFIG_INET6_XFRM_TUNNEL=m +CONFIG_INET6_TUNNEL=m +CONFIG_INET6_XFRM_MODE_TRANSPORT=m +CONFIG_INET6_XFRM_MODE_TUNNEL=m +CONFIG_INET6_XFRM_MODE_BEET=m +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +# CONFIG_IPV6_VTI is not set +CONFIG_IPV6_SIT=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_NDISC_NODETYPE=y +CONFIG_IPV6_TUNNEL=m +# CONFIG_IPV6_GRE is not set +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_NETWORK_SECMARK=y +CONFIG_NET_PTP_CLASSIFY=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_NETFILTER_ADVANCED=y +CONFIG_BRIDGE_NETFILTER=m + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_INGRESS=y +CONFIG_NETFILTER_NETLINK=m +CONFIG_NETFILTER_NETLINK_ACCT=m +CONFIG_NETFILTER_NETLINK_QUEUE=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_LOG_COMMON=m +CONFIG_NF_CONNTRACK_MARK=y +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_ZONES=y +CONFIG_NF_CONNTRACK_PROCFS=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_LABELS=y +CONFIG_NF_CT_PROTO_DCCP=m +CONFIG_NF_CT_PROTO_GRE=m +CONFIG_NF_CT_PROTO_SCTP=m +CONFIG_NF_CT_PROTO_UDPLITE=m +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_BROADCAST=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NF_CT_NETLINK_TIMEOUT=m +# CONFIG_NETFILTER_NETLINK_QUEUE_CT is not set +CONFIG_NF_NAT=m +CONFIG_NF_NAT_NEEDED=y +CONFIG_NF_NAT_PROTO_DCCP=m +CONFIG_NF_NAT_PROTO_UDPLITE=m +CONFIG_NF_NAT_PROTO_SCTP=m +CONFIG_NF_NAT_AMANDA=m +CONFIG_NF_NAT_FTP=m +CONFIG_NF_NAT_IRC=m +CONFIG_NF_NAT_SIP=m +CONFIG_NF_NAT_TFTP=m +CONFIG_NF_NAT_REDIRECT=m +CONFIG_NETFILTER_SYNPROXY=m +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=m +CONFIG_NF_TABLES_NETDEV=m +CONFIG_NFT_EXTHDR=m +CONFIG_NFT_META=m +CONFIG_NFT_CT=m +CONFIG_NFT_RBTREE=m +CONFIG_NFT_HASH=m +CONFIG_NFT_COUNTER=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_REJECT_INET=m +CONFIG_NFT_COMPAT=m +CONFIG_NETFILTER_XTABLES=m + +# +# Xtables combined modules +# +CONFIG_NETFILTER_XT_MARK=m +CONFIG_NETFILTER_XT_CONNMARK=m +CONFIG_NETFILTER_XT_SET=m + +# +# Xtables targets +# +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_CT=m +CONFIG_NETFILTER_XT_TARGET_DSCP=m +CONFIG_NETFILTER_XT_TARGET_HL=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_NAT=m +CONFIG_NETFILTER_XT_TARGET_NETMAP=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_NOTRACK=m +CONFIG_NETFILTER_XT_TARGET_RATEEST=m +CONFIG_NETFILTER_XT_TARGET_REDIRECT=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TPROXY=m +CONFIG_NETFILTER_XT_TARGET_TRACE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m + +# +# Xtables matches +# +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ECN=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_HL=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_L2TP=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_SOCKET=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_SET_MAX=256 +CONFIG_IP_SET_BITMAP_IP=m +CONFIG_IP_SET_BITMAP_IPMAC=m +CONFIG_IP_SET_BITMAP_PORT=m +CONFIG_IP_SET_HASH_IP=m +CONFIG_IP_SET_HASH_IPMARK=m +CONFIG_IP_SET_HASH_IPPORT=m +CONFIG_IP_SET_HASH_IPPORTIP=m +CONFIG_IP_SET_HASH_IPPORTNET=m +CONFIG_IP_SET_HASH_MAC=m +CONFIG_IP_SET_HASH_NETPORTNET=m +CONFIG_IP_SET_HASH_NET=m +CONFIG_IP_SET_HASH_NETNET=m +CONFIG_IP_SET_HASH_NETPORT=m +CONFIG_IP_SET_HASH_NETIFACE=m +CONFIG_IP_SET_LIST_SET=m +CONFIG_IP_VS=m +CONFIG_IP_VS_IPV6=y +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_AH_ESP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y +CONFIG_IP_VS_PROTO_SCTP=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +# CONFIG_IP_VS_FO is not set +CONFIG_IP_VS_OVF=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS SH scheduler +# +CONFIG_IP_VS_SH_TAB_BITS=8 + +# +# IPVS application helper +# +# CONFIG_IP_VS_FTP is not set +CONFIG_IP_VS_NFCT=y +CONFIG_IP_VS_PE_SIP=m + +# +# IP: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV4=m +CONFIG_NF_CONNTRACK_IPV4=m +# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set +CONFIG_NF_TABLES_IPV4=m +CONFIG_NFT_CHAIN_ROUTE_IPV4=m +CONFIG_NFT_REJECT_IPV4=m +CONFIG_NFT_DUP_IPV4=m +CONFIG_NF_TABLES_ARP=m +CONFIG_NF_DUP_IPV4=m +CONFIG_NF_LOG_ARP=m +CONFIG_NF_LOG_IPV4=m +CONFIG_NF_REJECT_IPV4=m +CONFIG_NF_NAT_IPV4=m +CONFIG_NFT_CHAIN_NAT_IPV4=m +CONFIG_NF_NAT_MASQUERADE_IPV4=m +CONFIG_NFT_MASQ_IPV4=m +CONFIG_NFT_REDIR_IPV4=m +CONFIG_NF_NAT_SNMP_BASIC=m +CONFIG_NF_NAT_PROTO_GRE=m +CONFIG_NF_NAT_PPTP=m +CONFIG_NF_NAT_H323=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m + +# +# IPv6: Netfilter Configuration +# +CONFIG_NF_DEFRAG_IPV6=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_NF_TABLES_IPV6=m +CONFIG_NFT_CHAIN_ROUTE_IPV6=m +CONFIG_NFT_REJECT_IPV6=m +CONFIG_NFT_DUP_IPV6=m +CONFIG_NF_DUP_IPV6=m +CONFIG_NF_REJECT_IPV6=m +CONFIG_NF_LOG_IPV6=m +CONFIG_NF_NAT_IPV6=m +CONFIG_NFT_CHAIN_NAT_IPV6=m +CONFIG_NF_NAT_MASQUERADE_IPV6=m +CONFIG_NFT_MASQ_IPV6=m +CONFIG_NFT_REDIR_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NFT_BRIDGE_META=m +CONFIG_NFT_BRIDGE_REJECT=m +CONFIG_NF_LOG_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_IP_DCCP=m +CONFIG_INET_DCCP_DIAG=m + +# +# DCCP CCIDs Configuration +# +# CONFIG_IP_DCCP_CCID2_DEBUG is not set +CONFIG_IP_DCCP_CCID3=y +# CONFIG_IP_DCCP_CCID3_DEBUG is not set +CONFIG_IP_DCCP_TFRC_LIB=y + +# +# DCCP Kernel Hacking +# +# CONFIG_IP_DCCP_DEBUG is not set +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_OBJCNT is not set +CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5=y +# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1 is not set +# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set +CONFIG_SCTP_COOKIE_HMAC_MD5=y +# CONFIG_SCTP_COOKIE_HMAC_SHA1 is not set +CONFIG_RDS=m +CONFIG_RDS_TCP=m +# CONFIG_RDS_DEBUG is not set +CONFIG_TIPC=m +CONFIG_TIPC_MEDIA_UDP=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +CONFIG_ATM_CLIP_NO_ICMP=y +# CONFIG_ATM_LANE is not set +CONFIG_ATM_BR2684=m +CONFIG_ATM_BR2684_IPFILTER=y +CONFIG_L2TP=m +# CONFIG_L2TP_DEBUGFS is not set +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_STP=y +CONFIG_GARP=y +CONFIG_MRP=y +CONFIG_BRIDGE=y +CONFIG_BRIDGE_IGMP_SNOOPING=y +CONFIG_BRIDGE_VLAN_FILTERING=y +CONFIG_HAVE_NET_DSA=y +CONFIG_NET_DSA=m +CONFIG_NET_DSA_HWMON=y +CONFIG_VLAN_8021Q=y +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +# CONFIG_DECNET is not set +CONFIG_LLC=y +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=m +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_PHONET is not set +CONFIG_6LOWPAN=m +CONFIG_6LOWPAN_NHC=m +CONFIG_6LOWPAN_NHC_DEST=m +CONFIG_6LOWPAN_NHC_FRAGMENT=m +CONFIG_6LOWPAN_NHC_HOP=m +CONFIG_6LOWPAN_NHC_IPV6=m +CONFIG_6LOWPAN_NHC_MOBILITY=m +CONFIG_6LOWPAN_NHC_ROUTING=m +CONFIG_6LOWPAN_NHC_UDP=m +# CONFIG_IEEE802154 is not set +CONFIG_NET_SCHED=y + +# +# Queueing/Scheduling +# +CONFIG_NET_SCH_CBQ=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_HFSC=m +# CONFIG_NET_SCH_ATM is not set +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +# CONFIG_NET_SCH_DRR is not set +CONFIG_NET_SCH_MQPRIO=m +# CONFIG_NET_SCH_CHOKE is not set +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=y +# CONFIG_NET_SCH_FQ is not set +# CONFIG_NET_SCH_HHF is not set +# CONFIG_NET_SCH_PIE is not set +CONFIG_NET_SCH_INGRESS=m +# CONFIG_NET_SCH_PLUG is not set + +# +# Classification +# +CONFIG_NET_CLS=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=y +CONFIG_NET_CLS_ROUTE4=y +CONFIG_NET_CLS_FW=y +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +# CONFIG_NET_CLS_RSVP is not set +# CONFIG_NET_CLS_RSVP6 is not set +CONFIG_NET_CLS_FLOW=m +# CONFIG_NET_CLS_CGROUP is not set +# CONFIG_NET_CLS_BPF is not set +# CONFIG_NET_CLS_FLOWER is not set +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_STACK=32 +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_CANID=m +# CONFIG_NET_EMATCH_IPSET is not set +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +# CONFIG_NET_ACT_GACT is not set +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_IPT=m +# CONFIG_NET_ACT_NAT is not set +# CONFIG_NET_ACT_PEDIT is not set +# CONFIG_NET_ACT_SIMP is not set +CONFIG_NET_ACT_SKBEDIT=m +# CONFIG_NET_ACT_CSUM is not set +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_CONNMARK=m +CONFIG_NET_CLS_IND=y +CONFIG_NET_SCH_FIFO=y +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +# CONFIG_BATMAN_ADV is not set +CONFIG_OPENVSWITCH=m +CONFIG_OPENVSWITCH_GRE=m +# CONFIG_VSOCKETS is not set +CONFIG_NETLINK_MMAP=y +CONFIG_NETLINK_DIAG=m +CONFIG_MPLS=y +CONFIG_NET_MPLS_GSO=m +# CONFIG_MPLS_ROUTING is not set +CONFIG_HSR=m +CONFIG_NET_SWITCHDEV=y +CONFIG_RPS=y +CONFIG_RFS_ACCEL=y +CONFIG_XPS=y +# CONFIG_CGROUP_NET_PRIO is not set +CONFIG_CGROUP_NET_CLASSID=y +CONFIG_NET_RX_BUSY_POLL=y +CONFIG_BQL=y +# CONFIG_BPF_JIT is not set +CONFIG_NET_FLOW_LIMIT=y + +# +# Network testing +# +CONFIG_NET_PKTGEN=m +# CONFIG_HAMRADIO is not set +CONFIG_CAN=y +CONFIG_CAN_RAW=m +CONFIG_CAN_BCM=m +CONFIG_CAN_GW=m + +# +# CAN Device Drivers +# +CONFIG_CAN_VCAN=m +CONFIG_CAN_SLCAN=m +CONFIG_CAN_DEV=m +CONFIG_CAN_CALC_BITTIMING=y +# CONFIG_CAN_LEDS is not set +CONFIG_CAN_TI_HECC=m +CONFIG_CAN_FLEXCAN=m +CONFIG_CAN_GRCAN=m +CONFIG_CAN_RCAR=m +CONFIG_CAN_SJA1000=m +CONFIG_CAN_SJA1000_ISA=m +CONFIG_CAN_SJA1000_PLATFORM=m +CONFIG_CAN_C_CAN=m +CONFIG_CAN_C_CAN_PLATFORM=m +CONFIG_CAN_M_CAN=m +CONFIG_CAN_CC770=m +CONFIG_CAN_CC770_ISA=m +CONFIG_CAN_CC770_PLATFORM=m + +# +# CAN SPI interfaces +# +CONFIG_CAN_MCP251X=m + +# +# CAN USB interfaces +# +CONFIG_CAN_EMS_USB=m +CONFIG_CAN_ESD_USB2=m +CONFIG_CAN_GS_USB=m +CONFIG_CAN_KVASER_USB=m +CONFIG_CAN_PEAK_USB=m +CONFIG_CAN_8DEV_USB=m +CONFIG_CAN_SOFTING=m +# CONFIG_CAN_DEBUG_DEVICES is not set +CONFIG_IRDA=m + +# +# IrDA protocols +# +CONFIG_IRLAN=m +# CONFIG_IRNET is not set +CONFIG_IRCOMM=m +CONFIG_IRDA_ULTRA=y + +# +# IrDA options +# +# CONFIG_IRDA_CACHE_LAST_LSAP is not set +# CONFIG_IRDA_FAST_RR is not set +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +CONFIG_IRTTY_SIR=m + +# +# Dongle support +# +# CONFIG_DONGLE is not set +CONFIG_KINGSUN_DONGLE=m +CONFIG_KSDAZZLE_DONGLE=m +CONFIG_KS959_DONGLE=m + +# +# FIR device drivers +# +CONFIG_USB_IRDA=m +CONFIG_SIGMATEL_FIR=m +CONFIG_MCS_FIR=m +CONFIG_BT=m +CONFIG_BT_BREDR=y +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=m +CONFIG_BT_HS=y +CONFIG_BT_LE=y +# CONFIG_BT_6LOWPAN is not set +# CONFIG_BT_SELFTEST is not set +CONFIG_BT_DEBUGFS=y + +# +# Bluetooth device drivers +# +CONFIG_BT_INTEL=m +CONFIG_BT_BCM=m +CONFIG_BT_RTL=m +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIBTUSB_BCM=y +CONFIG_BT_HCIBTUSB_RTL=y +CONFIG_BT_HCIBTSDIO=m +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_ATH3K=y +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIUART_3WIRE is not set +# CONFIG_BT_HCIUART_INTEL is not set +# CONFIG_BT_HCIUART_BCM is not set +# CONFIG_BT_HCIUART_QCA is not set +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +# CONFIG_BT_HCIVHCI is not set +CONFIG_BT_MRVL=m +CONFIG_BT_MRVL_SDIO=m +CONFIG_BT_ATH3K=m +CONFIG_AF_RXRPC=m +# CONFIG_AF_RXRPC_DEBUG is not set +CONFIG_RXKAD=m +CONFIG_FIB_RULES=y +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_PRIV=y +CONFIG_CFG80211=y +# CONFIG_NL80211_TESTMODE is not set +CONFIG_CFG80211_DEVELOPER_WARNINGS=y +# CONFIG_CFG80211_REG_DEBUG is not set +# CONFIG_CFG80211_CERTIFICATION_ONUS is not set +CONFIG_CFG80211_DEFAULT_PS=y +# CONFIG_CFG80211_DEBUGFS is not set +# CONFIG_CFG80211_INTERNAL_REGDB is not set +CONFIG_CFG80211_WEXT=y +# CONFIG_LIB80211 is not set +CONFIG_MAC80211=m +CONFIG_MAC80211_HAS_RC=y +CONFIG_MAC80211_RC_MINSTREL=y +CONFIG_MAC80211_RC_MINSTREL_HT=y +# CONFIG_MAC80211_RC_MINSTREL_VHT is not set +CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y +CONFIG_MAC80211_RC_DEFAULT="minstrel_ht" +CONFIG_MAC80211_MESH=y +CONFIG_MAC80211_LEDS=y +# CONFIG_MAC80211_DEBUGFS is not set +# CONFIG_MAC80211_MESSAGE_TRACING is not set +# CONFIG_MAC80211_DEBUG_MENU is not set +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0 +CONFIG_WIMAX=m +CONFIG_WIMAX_DEBUG_LEVEL=8 +CONFIG_RFKILL=y +CONFIG_RFKILL_LEDS=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_RFKILL_REGULATOR is not set +# CONFIG_RFKILL_GPIO is not set +# CONFIG_NET_9P is not set +# CONFIG_CAIF is not set +# CONFIG_CEPH_LIB is not set +# CONFIG_NFC is not set +CONFIG_LWTUNNEL=y +CONFIG_HAVE_BPF_JIT=y + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +CONFIG_FW_LOADER_USER_HELPER=y +CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y +CONFIG_WANT_DEV_COREDUMP=y +CONFIG_ALLOW_DEV_COREDUMP=y +CONFIG_DEV_COREDUMP=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_GENERIC_CPU_DEVICES is not set +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_IRQ=y +CONFIG_DMA_SHARED_BUFFER=y +# CONFIG_FENCE_TRACE is not set +# CONFIG_DMA_CMA is not set + +# +# Bus devices +# +CONFIG_ARM_CCI=y +CONFIG_ARM_CCI_PMU=y +CONFIG_ARM_CCI400_COMMON=y +CONFIG_ARM_CCI400_PMU=y +CONFIG_ARM_CCI500_PMU=y +# CONFIG_ARM_CCN is not set +# CONFIG_BRCMSTB_GISB_ARB is not set +# CONFIG_VEXPRESS_CONFIG is not set +# CONFIG_CONNECTOR is not set +# CONFIG_MTD is not set +CONFIG_DTC=y +CONFIG_OF=y +# CONFIG_OF_UNITTEST is not set +CONFIG_OF_FLATTREE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_IRQ=y +CONFIG_OF_NET=y +CONFIG_OF_MDIO=y +CONFIG_OF_RESERVED_MEM=y +# CONFIG_OF_OVERLAY is not set +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y +CONFIG_PARPORT=y +# CONFIG_PARPORT_PC is not set +# CONFIG_PARPORT_GSC is not set +# CONFIG_PARPORT_AX88796 is not set +# CONFIG_PARPORT_1284 is not set +CONFIG_PARPORT_NOT_PC=y +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_NULL_BLK is not set +CONFIG_ZRAM=m +CONFIG_ZRAM_LZ4_COMPRESS=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +CONFIG_BLK_DEV_CRYPTOLOOP=y +# CONFIG_BLK_DEV_DRBD is not set +CONFIG_BLK_DEV_NBD=m +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_BLK_DEV_RBD is not set + +# +# Misc devices +# +# CONFIG_SENSORS_LIS3LV02D is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_DUMMY_IRQ is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_APDS9802ALS is not set +# CONFIG_ISL29003 is not set +# CONFIG_ISL29020 is not set +CONFIG_SENSORS_TSL2550=m +# CONFIG_SENSORS_BH1780 is not set +# CONFIG_SENSORS_BH1770 is not set +# CONFIG_SENSORS_APDS990X is not set +# CONFIG_HMC6352 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +CONFIG_BMP085=y +CONFIG_BMP085_I2C=m +# CONFIG_BMP085_SPI is not set +# CONFIG_USB_SWITCH_FSA9480 is not set +# CONFIG_LATTICE_ECP3_CONFIG is not set +# CONFIG_SRAM is not set +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +CONFIG_EEPROM_93CX6=m +# CONFIG_EEPROM_93XX46 is not set + +# +# Texas Instruments shared transport line discipline +# +# CONFIG_TI_ST is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set + +# +# Altera FPGA firmware download module +# +# CONFIG_ALTERA_STAPL is not set + +# +# Intel MIC Bus Driver +# + +# +# SCIF Bus Driver +# + +# +# Intel MIC Host Driver +# + +# +# Intel MIC Card Driver +# + +# +# SCIF Driver +# +# CONFIG_ECHO is not set +# CONFIG_CXL_BASE is not set +# CONFIG_CXL_KERNEL_API is not set +# CONFIG_CXL_EEH is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +CONFIG_RAID_ATTRS=m +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_MQ_DEFAULT is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m +CONFIG_CHR_DEV_SCH=m +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +CONFIG_SCSI_ISCSI_ATTRS=y +# CONFIG_SCSI_SAS_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_ISCSI_BOOT_SYSFS is not set +# CONFIG_SCSI_UFSHCD is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +CONFIG_SCSI_OSD_INITIATOR=m +# CONFIG_SCSI_OSD_ULD is not set +CONFIG_SCSI_OSD_DPRINT_SENSE=1 +# CONFIG_SCSI_OSD_DEBUG is not set +CONFIG_ATA=y +# CONFIG_ATA_NONSTANDARD is not set +CONFIG_ATA_VERBOSE_ERROR=y +CONFIG_SATA_PMP=y + +# +# Controllers with non-SFF native interface +# +CONFIG_SATA_AHCI_PLATFORM=y +# CONFIG_AHCI_CEVA is not set +CONFIG_AHCI_SUNXI=y +CONFIG_ATA_SFF=y + +# +# SFF controllers with custom DMA interface +# +CONFIG_ATA_BMDMA=y + +# +# SATA SFF controllers with BMDMA +# + +# +# PATA SFF controllers with BMDMA +# + +# +# PIO-only SFF controllers +# +# CONFIG_PATA_PLATFORM is not set + +# +# Generic fallback / legacy drivers +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_MD_MULTIPATH=m +CONFIG_MD_FAULTY=m +CONFIG_BCACHE=m +# CONFIG_BCACHE_DEBUG is not set +# CONFIG_BCACHE_CLOSURES_DEBUG is not set +CONFIG_BLK_DEV_DM_BUILTIN=y +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_MQ_DEFAULT is not set +CONFIG_DM_DEBUG=y +CONFIG_DM_BUFIO=m +CONFIG_DM_BIO_PRISON=m +CONFIG_DM_PERSISTENT_DATA=m +# CONFIG_DM_DEBUG_BLOCK_STACK_TRACING is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_THIN_PROVISIONING=m +CONFIG_DM_CACHE=m +CONFIG_DM_CACHE_MQ=m +CONFIG_DM_CACHE_SMQ=m +CONFIG_DM_CACHE_CLEANER=m +# CONFIG_DM_ERA is not set +# CONFIG_DM_MIRROR is not set +CONFIG_DM_RAID=m +# CONFIG_DM_ZERO is not set +CONFIG_DM_MULTIPATH=m +# CONFIG_DM_MULTIPATH_QL is not set +# CONFIG_DM_MULTIPATH_ST is not set +# CONFIG_DM_DELAY is not set +CONFIG_DM_UEVENT=y +CONFIG_DM_FLAKEY=m +CONFIG_DM_VERITY=m +CONFIG_DM_SWITCH=m +CONFIG_DM_LOG_WRITES=m +# CONFIG_TARGET_CORE is not set +CONFIG_NETDEVICES=y +CONFIG_MII=y +CONFIG_NET_CORE=y +CONFIG_BONDING=y +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +# CONFIG_IFB is not set +# CONFIG_NET_TEAM is not set +CONFIG_MACVLAN=y +CONFIG_MACVTAP=m +CONFIG_IPVLAN=m +# CONFIG_VXLAN is not set +# CONFIG_GENEVE is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +CONFIG_TUN=y +# CONFIG_TUN_VNET_CROSS_LE is not set +CONFIG_VETH=y +# CONFIG_NLMON is not set +# CONFIG_NET_VRF is not set +CONFIG_ATM_DRIVERS=y +# CONFIG_ATM_DUMMY is not set +# CONFIG_ATM_TCP is not set + +# +# CAIF transport drivers +# + +# +# Distributed Switch Architecture drivers +# +# CONFIG_NET_DSA_MV88E6XXX is not set +# CONFIG_NET_DSA_MV88E6060 is not set +# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set +# CONFIG_NET_DSA_MV88E6131 is not set +# CONFIG_NET_DSA_MV88E6123_61_65 is not set +# CONFIG_NET_DSA_MV88E6171 is not set +# CONFIG_NET_DSA_MV88E6352 is not set +# CONFIG_NET_DSA_BCM_SF2 is not set +CONFIG_ETHERNET=y +CONFIG_NET_VENDOR_ALLWINNER=y +CONFIG_SUN4I_EMAC=y +# CONFIG_ALTERA_TSE is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_CADENCE is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_DM9000 is not set +# CONFIG_DNET is not set +CONFIG_NET_VENDOR_EZCHIP=y +# CONFIG_EZCHIP_NPS_MANAGEMENT_ENET is not set +# CONFIG_NET_VENDOR_FARADAY is not set +CONFIG_NET_VENDOR_HISILICON=y +# CONFIG_HIX5HD2_GMAC is not set +CONFIG_HIP04_ETH=m +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_ETHOC is not set +CONFIG_NET_VENDOR_QUALCOMM=y +# CONFIG_QCA7000 is not set +CONFIG_NET_VENDOR_RENESAS=y +CONFIG_NET_VENDOR_ROCKER=y +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SMSC is not set +CONFIG_NET_VENDOR_STMICRO=y +CONFIG_STMMAC_ETH=y +CONFIG_STMMAC_PLATFORM=y +CONFIG_DWMAC_GENERIC=y +# CONFIG_DWMAC_IPQ806X is not set +# CONFIG_DWMAC_LPC18XX is not set +# CONFIG_DWMAC_MESON is not set +# CONFIG_DWMAC_ROCKCHIP is not set +# CONFIG_DWMAC_SOCFPGA is not set +# CONFIG_DWMAC_STI is not set +CONFIG_DWMAC_SUNXI=m +CONFIG_NET_VENDOR_SYNOPSYS=y +CONFIG_SYNOPSYS_DWC_ETH_QOS=m +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PHYLIB=y +CONFIG_SWCONFIG=y +# CONFIG_SWCONFIG_LEDS is not set + +# +# MII PHY device drivers +# +CONFIG_AQUANTIA_PHY=m +CONFIG_AT803X_PHY=m +CONFIG_AMD_PHY=m +CONFIG_MARVELL_PHY=m +CONFIG_DAVICOM_PHY=m +CONFIG_QSEMI_PHY=m +CONFIG_LXT_PHY=m +CONFIG_CICADA_PHY=m +CONFIG_VITESSE_PHY=m +CONFIG_TERANETICS_PHY=m +CONFIG_SMSC_PHY=m +CONFIG_BROADCOM_PHY=m +# CONFIG_BCM7XXX_PHY is not set +CONFIG_BCM87XX_PHY=m +CONFIG_ICPLUS_PHY=m +CONFIG_REALTEK_PHY=m +CONFIG_NATIONAL_PHY=m +CONFIG_STE10XP=m +CONFIG_LSI_ET1011C_PHY=m +CONFIG_MICREL_PHY=m +# CONFIG_DP83867_PHY is not set +CONFIG_MICROCHIP_PHY=m +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_MDIO_SUN4I=y +# CONFIG_MDIO_BUS_MUX_GPIO is not set +# CONFIG_MDIO_BUS_MUX_MMIOREG is not set +# CONFIG_MDIO_BCM_UNIMAC is not set +CONFIG_B53=y +CONFIG_B53_SPI_DRIVER=y +CONFIG_B53_PHY_DRIVER=y +# CONFIG_B53_MMAP_DRIVER is not set +CONFIG_B53_SRAB_DRIVER=y +CONFIG_B53_PHY_FIXUP=y +# CONFIG_MICREL_KS8995MA is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPPOATM is not set +CONFIG_PPPOE=y +CONFIG_PPTP=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_SLIP=m +CONFIG_SLHC=y +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +CONFIG_USB_NET_DRIVERS=y +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_LAN78XX=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_AX88179_178A=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_HUAWEI_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SR9700=m +CONFIG_USB_NET_SR9800=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_GL620A=m +CONFIG_USB_NET_NET1080=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_MCS7830=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_NET_CDC_SUBSET=m +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_KC2190=y +CONFIG_USB_NET_ZAURUS=m +CONFIG_USB_NET_CX82310_ETH=m +CONFIG_USB_NET_KALMIA=m +CONFIG_USB_NET_QMI_WWAN=m +CONFIG_USB_HSO=m +CONFIG_USB_NET_INT51X1=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m +CONFIG_WLAN=y +CONFIG_LIBERTAS_THINFIRM=m +# CONFIG_LIBERTAS_THINFIRM_DEBUG is not set +CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_AT76C50X_USB=m +# CONFIG_USB_ZD1201 is not set +CONFIG_USB_NET_RNDIS_WLAN=m +CONFIG_RTL8187=m +CONFIG_RTL8187_LEDS=y +CONFIG_MAC80211_HWSIM=m +CONFIG_ATH_COMMON=m +CONFIG_ATH_CARDS=m +# CONFIG_ATH_DEBUG is not set +CONFIG_ATH9K_HW=m +CONFIG_ATH9K_COMMON=m +CONFIG_ATH9K_BTCOEX_SUPPORT=y +CONFIG_ATH9K=m +# CONFIG_ATH9K_AHB is not set +# CONFIG_ATH9K_DEBUGFS is not set +# CONFIG_ATH9K_DYNACK is not set +# CONFIG_ATH9K_WOW is not set +CONFIG_ATH9K_RFKILL=y +# CONFIG_ATH9K_CHANNEL_CONTEXT is not set +CONFIG_ATH9K_PCOEM=y +CONFIG_ATH9K_HTC=m +# CONFIG_ATH9K_HTC_DEBUGFS is not set +CONFIG_CARL9170=m +CONFIG_CARL9170_LEDS=y +CONFIG_CARL9170_WPC=y +# CONFIG_CARL9170_HWRNG is not set +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +# CONFIG_ATH6KL_DEBUG is not set +CONFIG_AR5523=m +CONFIG_ATH10K=m +# CONFIG_ATH10K_DEBUG is not set +# CONFIG_ATH10K_DEBUGFS is not set +# CONFIG_WCN36XX is not set +CONFIG_B43=m +CONFIG_B43_BCMA=y +CONFIG_B43_SSB=y +CONFIG_B43_BUSES_BCMA_AND_SSB=y +# CONFIG_B43_BUSES_BCMA is not set +# CONFIG_B43_BUSES_SSB is not set +# CONFIG_B43_SDIO is not set +CONFIG_B43_BCMA_PIO=y +CONFIG_B43_PIO=y +CONFIG_B43_PHY_G=y +CONFIG_B43_PHY_N=y +CONFIG_B43_PHY_LP=y +CONFIG_B43_PHY_HT=y +CONFIG_B43_LEDS=y +CONFIG_B43_HWRNG=y +# CONFIG_B43_DEBUG is not set +# CONFIG_B43LEGACY is not set +CONFIG_BRCMUTIL=m +# CONFIG_BRCMSMAC is not set +CONFIG_BRCMFMAC=m +CONFIG_BRCMFMAC_PROTO_BCDC=y +CONFIG_BRCMFMAC_SDIO=y +# CONFIG_BRCMFMAC_USB is not set +# CONFIG_BRCM_TRACING is not set +# CONFIG_BRCMDBG is not set +# CONFIG_HOSTAP is not set +# CONFIG_LIBERTAS is not set +# CONFIG_P54_COMMON is not set +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +# CONFIG_RT73USB is not set +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT33XX=y +CONFIG_RT2800USB_RT35XX=y +CONFIG_RT2800USB_RT3573=y +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RT2800_LIB=m +CONFIG_RT2X00_LIB_USB=m +CONFIG_RT2X00_LIB=m +CONFIG_RT2X00_LIB_FIRMWARE=y +CONFIG_RT2X00_LIB_CRYPTO=y +CONFIG_RT2X00_LIB_LEDS=y +CONFIG_RT2X00_DEBUG=y +CONFIG_WL_MEDIATEK=y +CONFIG_MT7601U=m +CONFIG_RTL_CARDS=m +CONFIG_RTL8192CU=m +CONFIG_RTLWIFI=m +CONFIG_RTLWIFI_USB=m +CONFIG_RTLWIFI_DEBUG=y +CONFIG_RTL8192C_COMMON=m +# CONFIG_WL_TI is not set +# CONFIG_ZD1211RW is not set +# CONFIG_MWIFIEX is not set +# CONFIG_CW1200 is not set +# CONFIG_RSI_91X is not set + +# +# WiMAX Wireless Broadband devices +# +# CONFIG_WIMAX_I2400M_USB is not set +# CONFIG_WAN is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +CONFIG_INPUT_LEDS=m +CONFIG_INPUT_FF_MEMLESS=m +CONFIG_INPUT_POLLDEV=m +# CONFIG_INPUT_SPARSEKMAP is not set +CONFIG_INPUT_MATRIXKMAP=m + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=m +CONFIG_INPUT_EVBUG=m + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +# CONFIG_KEYBOARD_ADP5589 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_QT1070 is not set +# CONFIG_KEYBOARD_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_GPIO_POLLED is not set +# CONFIG_KEYBOARD_TCA6416 is not set +# CONFIG_KEYBOARD_TCA8418 is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_LM8323 is not set +# CONFIG_KEYBOARD_LM8333 is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_MCS is not set +# CONFIG_KEYBOARD_MPR121 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +# CONFIG_KEYBOARD_SAMSUNG is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +CONFIG_KEYBOARD_SUN4I_LRADC=m +# CONFIG_KEYBOARD_OMAP4 is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_CAP11XX is not set +CONFIG_KEYBOARD_BCM=m +CONFIG_INPUT_MOUSE=y +CONFIG_MOUSE_PS2=y +CONFIG_MOUSE_PS2_ALPS=y +CONFIG_MOUSE_PS2_LOGIPS2PP=y +CONFIG_MOUSE_PS2_SYNAPTICS=y +CONFIG_MOUSE_PS2_CYPRESS=y +CONFIG_MOUSE_PS2_TRACKPOINT=y +# CONFIG_MOUSE_PS2_ELANTECH is not set +# CONFIG_MOUSE_PS2_SENTELIC is not set +# CONFIG_MOUSE_PS2_TOUCHKIT is not set +CONFIG_MOUSE_PS2_FOCALTECH=y +CONFIG_MOUSE_SERIAL=m +# CONFIG_MOUSE_APPLETOUCH is not set +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_ELAN_I2C is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_MOUSE_GPIO is not set +# CONFIG_MOUSE_SYNAPTICS_I2C is not set +# CONFIG_MOUSE_SYNAPTICS_USB is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_PROPERTIES=y +# CONFIG_TOUCHSCREEN_ADS7846 is not set +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_AR1021_I2C is not set +# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set +# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set +# CONFIG_TOUCHSCREEN_BU21013 is not set +CONFIG_TOUCHSCREEN_CHIPONE_ICN8318=m +# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set +# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set +# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_EGALAX is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +CONFIG_TOUCHSCREEN_GOODIX=m +# CONFIG_TOUCHSCREEN_ILI210X is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELAN is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_WACOM_I2C is not set +# CONFIG_TOUCHSCREEN_MAX11801 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MMS114 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +CONFIG_TOUCHSCREEN_IMX6UL_TSC=m +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_PIXCIR is not set +# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set +# CONFIG_TOUCHSCREEN_WM97XX is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +# CONFIG_TOUCHSCREEN_TSC_SERIO is not set +# CONFIG_TOUCHSCREEN_TSC2005 is not set +# CONFIG_TOUCHSCREEN_TSC2007 is not set +# CONFIG_TOUCHSCREEN_ST1232 is not set +CONFIG_TOUCHSCREEN_SUN4I=m +# CONFIG_TOUCHSCREEN_SUR40 is not set +CONFIG_TOUCHSCREEN_SX8654=m +# CONFIG_TOUCHSCREEN_TPS6507X is not set +# CONFIG_TOUCHSCREEN_ZFORCE is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_AD714X is not set +# CONFIG_INPUT_BMA150 is not set +# CONFIG_INPUT_E3X0_BUTTON is not set +# CONFIG_INPUT_MMA8450 is not set +# CONFIG_INPUT_MPU3050 is not set +# CONFIG_INPUT_GP2A is not set +# CONFIG_INPUT_GPIO_BEEPER is not set +# CONFIG_INPUT_GPIO_TILT_POLLED is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_KXTJ9 is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_REGULATOR_HAPTIC is not set +CONFIG_INPUT_AXP20X_PEK=y +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_PCF8574 is not set +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +# CONFIG_INPUT_ADXL34X is not set +# CONFIG_INPUT_IMS_PCU is not set +# CONFIG_INPUT_CMA3000 is not set +# CONFIG_INPUT_DRV260X_HAPTICS is not set +# CONFIG_INPUT_DRV2665_HAPTICS is not set +# CONFIG_INPUT_DRV2667_HAPTICS is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_PARKBD is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_SERIO_PS2MULT is not set +# CONFIG_SERIO_ARC_PS2 is not set +# CONFIG_SERIO_APBPS2 is not set +CONFIG_SERIO_SUN4I_PS2=m +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_TTY=y +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_VT_CONSOLE_SLEEP=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_UNIX98_PTYS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_N_GSM is not set +# CONFIG_TRACE_SINK is not set +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y + +# +# Serial drivers +# +CONFIG_SERIAL_EARLYCON=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=8 +CONFIG_SERIAL_8250_RUNTIME_UARTS=8 +# CONFIG_SERIAL_8250_EXTENDED is not set +CONFIG_SERIAL_8250_DW=y +# CONFIG_SERIAL_8250_EM is not set +# CONFIG_SERIAL_8250_INGENIC is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set +# CONFIG_SERIAL_MAX3100 is not set +# CONFIG_SERIAL_MAX310X is not set +CONFIG_SERIAL_UARTLITE=m +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SERIAL_SCCNXP is not set +# CONFIG_SERIAL_SC16IS7XX is not set +# CONFIG_SERIAL_BCM63XX is not set +# CONFIG_SERIAL_ALTERA_JTAGUART is not set +# CONFIG_SERIAL_ALTERA_UART is not set +# CONFIG_SERIAL_IFX6X60 is not set +# CONFIG_SERIAL_XILINX_PS_UART is not set +# CONFIG_SERIAL_ARC is not set +# CONFIG_SERIAL_FSL_LPUART is not set +CONFIG_SERIAL_CONEXANT_DIGICOLOR=m +# CONFIG_SERIAL_ST_ASC is not set +CONFIG_SERIAL_STM32=m +# CONFIG_TTY_PRINTK is not set +# CONFIG_PRINTER is not set +# CONFIG_PPDEV is not set +# CONFIG_HVC_DCC is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=m +# CONFIG_HW_RANDOM_TIMERIOMEM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_XILLYBUS is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_MUX=y + +# +# Multiplexer I2C Chip support +# +CONFIG_I2C_ARB_GPIO_CHALLENGE=m +CONFIG_I2C_MUX_GPIO=m +CONFIG_I2C_MUX_PCA9541=m +CONFIG_I2C_MUX_PCA954x=m +CONFIG_I2C_MUX_PINCTRL=m +CONFIG_I2C_MUX_REG=m +CONFIG_I2C_HELPER_AUTO=y +CONFIG_I2C_ALGOBIT=m + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_CBUS_GPIO is not set +# CONFIG_I2C_DESIGNWARE_PLATFORM is not set +CONFIG_I2C_EMEV2=m +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_MV64XXX=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_PXA_PCI is not set +# CONFIG_I2C_RK3X is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_SUN6I_P2WI is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_DIOLAN_U2C is not set +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_ROBOTFUZZ_OSIF is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +CONFIG_I2C_STUB=m +CONFIG_I2C_SLAVE=y +CONFIG_I2C_SLAVE_EEPROM=m +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_ALTERA is not set +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_BUTTERFLY is not set +# CONFIG_SPI_CADENCE is not set +# CONFIG_SPI_GPIO is not set +# CONFIG_SPI_LM70_LLP is not set +# CONFIG_SPI_FSL_SPI is not set +# CONFIG_SPI_OC_TINY is not set +# CONFIG_SPI_PXA2XX_PCI is not set +# CONFIG_SPI_ROCKCHIP is not set +# CONFIG_SPI_SC18IS602 is not set +CONFIG_SPI_SUN4I=y +CONFIG_SPI_SUN6I=y +# CONFIG_SPI_XCOMM is not set +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_ZYNQMP_GQSPI is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +CONFIG_SPI_SPIDEV=y +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_SPMI is not set +# CONFIG_HSI is not set + +# +# PPS support +# +CONFIG_PPS=y +# CONFIG_PPS_DEBUG is not set + +# +# PPS clients support +# +# CONFIG_PPS_CLIENT_KTIMER is not set +# CONFIG_PPS_CLIENT_LDISC is not set +# CONFIG_PPS_CLIENT_PARPORT is not set +# CONFIG_PPS_CLIENT_GPIO is not set + +# +# PPS generators support +# + +# +# PTP clock support +# +CONFIG_PTP_1588_CLOCK=y +# CONFIG_DP83640_PHY is not set +CONFIG_PINCTRL=y + +# +# Pin controllers +# +CONFIG_PINMUX=y +CONFIG_PINCONF=y +CONFIG_GENERIC_PINCONF=y +# CONFIG_DEBUG_PINCTRL is not set +# CONFIG_PINCTRL_AMD is not set +# CONFIG_PINCTRL_SINGLE is not set +CONFIG_PINCTRL_SUNXI_COMMON=y +CONFIG_PINCTRL_SUN4I_A10=y +CONFIG_PINCTRL_SUN5I_A10S=y +CONFIG_PINCTRL_SUN5I_A13=y +CONFIG_PINCTRL_SUN6I_A31=y +CONFIG_PINCTRL_SUN6I_A31S=y +CONFIG_PINCTRL_SUN6I_A31_R=y +CONFIG_PINCTRL_SUN7I_A20=y +CONFIG_PINCTRL_SUN8I_A23=y +CONFIG_PINCTRL_SUN8I_A33=y +CONFIG_PINCTRL_SUN8I_A23_R=y +CONFIG_PINCTRL_SUN9I_A80=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_OF_GPIO=y +CONFIG_GPIOLIB_IRQCHIP=y +# CONFIG_DEBUG_GPIO is not set +CONFIG_GPIO_SYSFS=y + +# +# Memory mapped GPIO drivers +# +# CONFIG_GPIO_74XX_MMIO is not set +CONFIG_GPIO_ALTERA=m +# CONFIG_GPIO_DWAPB is not set +# CONFIG_GPIO_EM is not set +# CONFIG_GPIO_GENERIC_PLATFORM is not set +# CONFIG_GPIO_GRGPIO is not set +# CONFIG_GPIO_SCH311X is not set +CONFIG_GPIO_SYSCON=m +# CONFIG_GPIO_ZEVIO is not set + +# +# I2C GPIO expanders +# +# CONFIG_GPIO_ADP5588 is not set +# CONFIG_GPIO_ADNP is not set +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +# CONFIG_GPIO_PCA953X is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_SX150X is not set + +# +# MFD GPIO expanders +# +# CONFIG_GPIO_UCB1400 is not set + +# +# SPI GPIO expanders +# +# CONFIG_GPIO_74X164 is not set +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_MC33880 is not set +# CONFIG_GPIO_ZX is not set + +# +# USB GPIO expanders +# +CONFIG_W1=m + +# +# 1-wire Bus Masters +# +# CONFIG_W1_MASTER_DS2490 is not set +# CONFIG_W1_MASTER_DS2482 is not set +# CONFIG_W1_MASTER_DS1WM is not set +# CONFIG_W1_MASTER_GPIO is not set + +# +# 1-wire Slaves +# +# CONFIG_W1_SLAVE_THERM is not set +# CONFIG_W1_SLAVE_SMEM is not set +# CONFIG_W1_SLAVE_DS2408 is not set +# CONFIG_W1_SLAVE_DS2413 is not set +# CONFIG_W1_SLAVE_DS2406 is not set +# CONFIG_W1_SLAVE_DS2423 is not set +# CONFIG_W1_SLAVE_DS2431 is not set +# CONFIG_W1_SLAVE_DS2433 is not set +# CONFIG_W1_SLAVE_DS2760 is not set +# CONFIG_W1_SLAVE_DS2780 is not set +# CONFIG_W1_SLAVE_DS2781 is not set +# CONFIG_W1_SLAVE_DS28E04 is not set +# CONFIG_W1_SLAVE_BQ27000 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_TEST_POWER is not set +# CONFIG_BATTERY_DS2780 is not set +# CONFIG_BATTERY_DS2781 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_SBS is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +# CONFIG_BATTERY_MAX17042 is not set +# CONFIG_CHARGER_ISP1704 is not set +# CONFIG_CHARGER_MAX8903 is not set +# CONFIG_CHARGER_LP8727 is not set +# CONFIG_CHARGER_GPIO is not set +# CONFIG_CHARGER_MANAGER is not set +# CONFIG_CHARGER_BQ2415X is not set +# CONFIG_CHARGER_BQ24190 is not set +# CONFIG_CHARGER_BQ24257 is not set +# CONFIG_CHARGER_BQ24735 is not set +# CONFIG_CHARGER_BQ25890 is not set +# CONFIG_CHARGER_SMB347 is not set +CONFIG_BATTERY_GAUGE_LTC2941=m +CONFIG_BATTERY_RT5033=m +# CONFIG_CHARGER_RT9455 is not set +CONFIG_AXP20X_POWER=y +CONFIG_POWER_RESET=y +# CONFIG_POWER_RESET_BRCMSTB is not set +# CONFIG_POWER_RESET_GPIO is not set +# CONFIG_POWER_RESET_GPIO_RESTART is not set +# CONFIG_POWER_RESET_LTC2952 is not set +# CONFIG_POWER_RESET_RESTART is not set +CONFIG_POWER_RESET_VERSATILE=y +# CONFIG_POWER_RESET_SYSCON is not set +CONFIG_POWER_RESET_SYSCON_POWEROFF=y +# CONFIG_POWER_AVS is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7314 is not set +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7310 is not set +# CONFIG_SENSORS_ADT7410 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS620 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_G762 is not set +# CONFIG_SENSORS_GPIO_FAN is not set +# CONFIG_SENSORS_HIH6130 is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_JC42 is not set +# CONFIG_SENSORS_POWR1220 is not set +# CONFIG_SENSORS_LINEAGE is not set +# CONFIG_SENSORS_LTC2945 is not set +# CONFIG_SENSORS_LTC4151 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4222 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LTC4260 is not set +# CONFIG_SENSORS_LTC4261 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX16065 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX1668 is not set +# CONFIG_SENSORS_MAX197 is not set +# CONFIG_SENSORS_MAX6639 is not set +# CONFIG_SENSORS_MAX6642 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_MAX6697 is not set +# CONFIG_SENSORS_HTU21 is not set +# CONFIG_SENSORS_MCP3021 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LM95234 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_LM95245 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_NTC_THERMISTOR is not set +# CONFIG_SENSORS_NCT6683 is not set +# CONFIG_SENSORS_NCT6775 is not set +# CONFIG_SENSORS_NCT7802 is not set +CONFIG_SENSORS_NCT7904=m +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_PMBUS is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_SHT21 is not set +# CONFIG_SENSORS_SHTC1 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_EMC1403 is not set +# CONFIG_SENSORS_EMC2103 is not set +# CONFIG_SENSORS_EMC6W201 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_SCH56XX_COMMON is not set +# CONFIG_SENSORS_SCH5627 is not set +# CONFIG_SENSORS_SCH5636 is not set +# CONFIG_SENSORS_SMM665 is not set +# CONFIG_SENSORS_ADC128D818 is not set +# CONFIG_SENSORS_ADS1015 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_ADS7871 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_INA209 is not set +# CONFIG_SENSORS_INA2XX is not set +# CONFIG_SENSORS_TC74 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP102 is not set +# CONFIG_SENSORS_TMP103 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83795 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +# CONFIG_THERMAL_WRITABLE_TRIPS is not set +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set +# CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR is not set +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set +CONFIG_THERMAL_GOV_STEP_WISE=y +# CONFIG_THERMAL_GOV_BANG_BANG is not set +# CONFIG_THERMAL_GOV_USER_SPACE is not set +# CONFIG_THERMAL_GOV_POWER_ALLOCATOR is not set +# CONFIG_CPU_THERMAL is not set +# CONFIG_CLOCK_THERMAL is not set +# CONFIG_THERMAL_EMULATION is not set +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_DA9062_WATCHDOG=m +# CONFIG_GPIO_WATCHDOG is not set +# CONFIG_XILINX_WATCHDOG is not set +# CONFIG_CADENCE_WATCHDOG is not set +# CONFIG_DW_WATCHDOG is not set +CONFIG_SUNXI_WATCHDOG=y +# CONFIG_MAX63XX_WATCHDOG is not set +# CONFIG_MEN_A21_WDT is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +CONFIG_SSB=m +CONFIG_SSB_BLOCKIO=y +CONFIG_SSB_SDIOHOST_POSSIBLE=y +# CONFIG_SSB_SDIOHOST is not set +# CONFIG_SSB_SILENT is not set +# CONFIG_SSB_DEBUG is not set +# CONFIG_SSB_DRIVER_GPIO is not set +CONFIG_BCMA_POSSIBLE=y + +# +# Broadcom specific AMBA +# +CONFIG_BCMA=m +CONFIG_BCMA_BLOCKIO=y +# CONFIG_BCMA_HOST_SOC is not set +# CONFIG_BCMA_DRIVER_GMAC_CMN is not set +# CONFIG_BCMA_DRIVER_GPIO is not set +# CONFIG_BCMA_DEBUG is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_AS3711 is not set +# CONFIG_MFD_AS3722 is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_AAT2870_CORE is not set +# CONFIG_MFD_ATMEL_HLCDC is not set +# CONFIG_MFD_BCM590XX is not set +CONFIG_MFD_AXP20X=y +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_DA9052_SPI is not set +# CONFIG_MFD_DA9052_I2C is not set +# CONFIG_MFD_DA9055 is not set +CONFIG_MFD_DA9062=m +# CONFIG_MFD_DA9063 is not set +CONFIG_MFD_DA9150=m +# CONFIG_MFD_DLN2 is not set +# CONFIG_MFD_MC13XXX_SPI is not set +# CONFIG_MFD_MC13XXX_I2C is not set +# CONFIG_MFD_HI6421_PMIC is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_INTEL_SOC_PMIC is not set +# CONFIG_MFD_KEMPLD is not set +# CONFIG_MFD_88PM800 is not set +# CONFIG_MFD_88PM805 is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_MAX14577 is not set +# CONFIG_MFD_MAX77686 is not set +# CONFIG_MFD_MAX77693 is not set +# CONFIG_MFD_MAX77843 is not set +# CONFIG_MFD_MAX8907 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_MAX8997 is not set +# CONFIG_MFD_MAX8998 is not set +CONFIG_MFD_MT6397=m +# CONFIG_MFD_MENF21BMC is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_MFD_VIPERBOARD is not set +# CONFIG_MFD_RETU is not set +# CONFIG_MFD_PCF50633 is not set +CONFIG_UCB1400_CORE=m +# CONFIG_MFD_PM8921_CORE is not set +CONFIG_MFD_RT5033=m +# CONFIG_MFD_RTSX_USB is not set +# CONFIG_MFD_RC5T583 is not set +# CONFIG_MFD_RK808 is not set +# CONFIG_MFD_RN5T618 is not set +# CONFIG_MFD_SEC_CORE is not set +# CONFIG_MFD_SI476X_CORE is not set +# CONFIG_MFD_SM501 is not set +CONFIG_MFD_SKY81452=m +# CONFIG_MFD_SMSC is not set +# CONFIG_ABX500_CORE is not set +# CONFIG_MFD_STMPE is not set +CONFIG_MFD_SUN6I_PRCM=y +CONFIG_MFD_SYSCON=y +# CONFIG_MFD_TI_AM335X_TSCADC is not set +# CONFIG_MFD_LP3943 is not set +# CONFIG_MFD_LP8788 is not set +# CONFIG_MFD_PALMAS is not set +# CONFIG_TPS6105X is not set +# CONFIG_TPS65010 is not set +# CONFIG_TPS6507X is not set +# CONFIG_MFD_TPS65090 is not set +# CONFIG_MFD_TPS65217 is not set +# CONFIG_MFD_TPS65218 is not set +# CONFIG_MFD_TPS6586X is not set +# CONFIG_MFD_TPS65910 is not set +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_TPS65912_I2C is not set +# CONFIG_MFD_TPS65912_SPI is not set +# CONFIG_MFD_TPS80031 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_TWL6040_CORE is not set +CONFIG_MFD_WL1273_CORE=m +# CONFIG_MFD_LM3533 is not set +# CONFIG_MFD_TC3589X is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_MFD_ARIZONA_I2C is not set +# CONFIG_MFD_ARIZONA_SPI is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X_I2C is not set +# CONFIG_MFD_WM831X_SPI is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +CONFIG_REGULATOR=y +# CONFIG_REGULATOR_DEBUG is not set +CONFIG_REGULATOR_FIXED_VOLTAGE=y +CONFIG_REGULATOR_VIRTUAL_CONSUMER=y +# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set +# CONFIG_REGULATOR_ACT8865 is not set +# CONFIG_REGULATOR_AD5398 is not set +CONFIG_REGULATOR_ANATOP=m +CONFIG_REGULATOR_AXP20X=y +CONFIG_REGULATOR_DA9062=m +# CONFIG_REGULATOR_DA9210 is not set +# CONFIG_REGULATOR_DA9211 is not set +# CONFIG_REGULATOR_FAN53555 is not set +CONFIG_REGULATOR_GPIO=y +# CONFIG_REGULATOR_ISL9305 is not set +# CONFIG_REGULATOR_ISL6271A is not set +# CONFIG_REGULATOR_LP3971 is not set +# CONFIG_REGULATOR_LP3972 is not set +# CONFIG_REGULATOR_LP872X is not set +# CONFIG_REGULATOR_LP8755 is not set +# CONFIG_REGULATOR_LTC3589 is not set +# CONFIG_REGULATOR_MAX1586 is not set +# CONFIG_REGULATOR_MAX8649 is not set +# CONFIG_REGULATOR_MAX8660 is not set +# CONFIG_REGULATOR_MAX8952 is not set +# CONFIG_REGULATOR_MAX8973 is not set +CONFIG_REGULATOR_MT6311=m +CONFIG_REGULATOR_MT6397=m +# CONFIG_REGULATOR_PFUZE100 is not set +CONFIG_REGULATOR_RT5033=m +CONFIG_REGULATOR_SKY81452=m +# CONFIG_REGULATOR_TPS51632 is not set +# CONFIG_REGULATOR_TPS62360 is not set +# CONFIG_REGULATOR_TPS65023 is not set +# CONFIG_REGULATOR_TPS6507X is not set +# CONFIG_REGULATOR_TPS6524X is not set +CONFIG_MEDIA_SUPPORT=y + +# +# Multimedia core support +# +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_MEDIA_ANALOG_TV_SUPPORT=y +CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y +CONFIG_MEDIA_RADIO_SUPPORT=y +CONFIG_MEDIA_SDR_SUPPORT=y +CONFIG_MEDIA_RC_SUPPORT=y +CONFIG_MEDIA_CONTROLLER=y +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_SUBDEV_API=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_ADV_DEBUG=y +CONFIG_VIDEO_FIXED_MINOR_RANGES=y +CONFIG_VIDEO_TUNER=m +CONFIG_V4L2_MEM2MEM_DEV=m +CONFIG_V4L2_FLASH_LED_CLASS=m +CONFIG_VIDEOBUF_GEN=m +CONFIG_VIDEOBUF_VMALLOC=m +CONFIG_VIDEOBUF_DVB=m +CONFIG_VIDEOBUF2_CORE=m +CONFIG_VIDEOBUF2_MEMOPS=m +CONFIG_VIDEOBUF2_VMALLOC=m +CONFIG_DVB_CORE=y +CONFIG_DVB_NET=y +CONFIG_TTPCI_EEPROM=m +CONFIG_DVB_MAX_ADAPTERS=8 +CONFIG_DVB_DYNAMIC_MINORS=y + +# +# Media drivers +# +CONFIG_RC_CORE=y +CONFIG_RC_MAP=y +CONFIG_RC_DECODERS=y +CONFIG_LIRC=m +CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_NEC_DECODER=y +CONFIG_IR_RC5_DECODER=y +CONFIG_IR_RC6_DECODER=y +CONFIG_IR_JVC_DECODER=y +CONFIG_IR_SONY_DECODER=y +CONFIG_IR_SANYO_DECODER=y +CONFIG_IR_SHARP_DECODER=y +CONFIG_IR_MCE_KBD_DECODER=y +CONFIG_IR_XMP_DECODER=y +CONFIG_RC_DEVICES=y +# CONFIG_RC_ATI_REMOTE is not set +# CONFIG_IR_HIX5HD2 is not set +# CONFIG_IR_IMON is not set +# CONFIG_IR_MCEUSB is not set +# CONFIG_IR_REDRAT3 is not set +# CONFIG_IR_STREAMZAP is not set +# CONFIG_IR_IGORPLUGUSB is not set +# CONFIG_IR_IGUANA is not set +# CONFIG_IR_TTUSBIR is not set +# CONFIG_RC_LOOPBACK is not set +# CONFIG_IR_GPIO_CIR is not set +CONFIG_IR_SUNXI=m +CONFIG_MEDIA_USB_SUPPORT=y + +# +# Webcam devices +# +# CONFIG_USB_VIDEO_CLASS is not set +CONFIG_USB_GSPCA=m +# CONFIG_USB_M5602 is not set +# CONFIG_USB_STV06XX is not set +# CONFIG_USB_GL860 is not set +# CONFIG_USB_GSPCA_BENQ is not set +# CONFIG_USB_GSPCA_CONEX is not set +# CONFIG_USB_GSPCA_CPIA1 is not set +# CONFIG_USB_GSPCA_DTCS033 is not set +# CONFIG_USB_GSPCA_ETOMS is not set +# CONFIG_USB_GSPCA_FINEPIX is not set +# CONFIG_USB_GSPCA_JEILINJ is not set +# CONFIG_USB_GSPCA_JL2005BCD is not set +# CONFIG_USB_GSPCA_KINECT is not set +# CONFIG_USB_GSPCA_KONICA is not set +# CONFIG_USB_GSPCA_MARS is not set +# CONFIG_USB_GSPCA_MR97310A is not set +# CONFIG_USB_GSPCA_NW80X is not set +# CONFIG_USB_GSPCA_OV519 is not set +# CONFIG_USB_GSPCA_OV534 is not set +# CONFIG_USB_GSPCA_OV534_9 is not set +# CONFIG_USB_GSPCA_PAC207 is not set +# CONFIG_USB_GSPCA_PAC7302 is not set +# CONFIG_USB_GSPCA_PAC7311 is not set +# CONFIG_USB_GSPCA_SE401 is not set +# CONFIG_USB_GSPCA_SN9C2028 is not set +# CONFIG_USB_GSPCA_SN9C20X is not set +# CONFIG_USB_GSPCA_SONIXB is not set +# CONFIG_USB_GSPCA_SONIXJ is not set +# CONFIG_USB_GSPCA_SPCA500 is not set +# CONFIG_USB_GSPCA_SPCA501 is not set +# CONFIG_USB_GSPCA_SPCA505 is not set +# CONFIG_USB_GSPCA_SPCA506 is not set +# CONFIG_USB_GSPCA_SPCA508 is not set +# CONFIG_USB_GSPCA_SPCA561 is not set +# CONFIG_USB_GSPCA_SPCA1528 is not set +# CONFIG_USB_GSPCA_SQ905 is not set +# CONFIG_USB_GSPCA_SQ905C is not set +# CONFIG_USB_GSPCA_SQ930X is not set +# CONFIG_USB_GSPCA_STK014 is not set +# CONFIG_USB_GSPCA_STK1135 is not set +# CONFIG_USB_GSPCA_STV0680 is not set +# CONFIG_USB_GSPCA_SUNPLUS is not set +# CONFIG_USB_GSPCA_T613 is not set +# CONFIG_USB_GSPCA_TOPRO is not set +# CONFIG_USB_GSPCA_TOUPTEK is not set +# CONFIG_USB_GSPCA_TV8532 is not set +# CONFIG_USB_GSPCA_VC032X is not set +# CONFIG_USB_GSPCA_VICAM is not set +# CONFIG_USB_GSPCA_XIRLINK_CIT is not set +# CONFIG_USB_GSPCA_ZC3XX is not set +# CONFIG_USB_PWC is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +# CONFIG_VIDEO_USBTV is not set + +# +# Analog TV USB devices +# +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_PVRUSB2_SYSFS=y +CONFIG_VIDEO_PVRUSB2_DVB=y +# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_USBVISION=m +CONFIG_VIDEO_STK1160_COMMON=m +# CONFIG_VIDEO_STK1160_AC97 is not set +CONFIG_VIDEO_STK1160=m +# CONFIG_VIDEO_GO7007 is not set + +# +# Analog/digital TV USB devices +# +CONFIG_VIDEO_AU0828=m +CONFIG_VIDEO_AU0828_V4L2=y +# CONFIG_VIDEO_AU0828_RC is not set +CONFIG_VIDEO_CX231XX=m +CONFIG_VIDEO_CX231XX_RC=y +CONFIG_VIDEO_CX231XX_ALSA=m +CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_VIDEO_TM6000=m +CONFIG_VIDEO_TM6000_ALSA=m +CONFIG_VIDEO_TM6000_DVB=m + +# +# Digital TV USB devices +# +CONFIG_DVB_USB=m +CONFIG_DVB_USB_DEBUG=y +CONFIG_DVB_USB_A800=m +CONFIG_DVB_USB_DIBUSB_MB=m +CONFIG_DVB_USB_DIBUSB_MB_FAULTY=y +CONFIG_DVB_USB_DIBUSB_MC=m +CONFIG_DVB_USB_DIB0700=m +CONFIG_DVB_USB_UMT_010=m +CONFIG_DVB_USB_CXUSB=m +CONFIG_DVB_USB_M920X=m +CONFIG_DVB_USB_DIGITV=m +CONFIG_DVB_USB_VP7045=m +CONFIG_DVB_USB_VP702X=m +CONFIG_DVB_USB_GP8PSK=m +CONFIG_DVB_USB_NOVA_T_USB2=m +CONFIG_DVB_USB_TTUSB2=m +CONFIG_DVB_USB_DTT200U=m +CONFIG_DVB_USB_OPERA1=m +CONFIG_DVB_USB_AF9005=m +CONFIG_DVB_USB_AF9005_REMOTE=m +CONFIG_DVB_USB_PCTV452E=m +CONFIG_DVB_USB_DW2102=m +CONFIG_DVB_USB_CINERGY_T2=m +CONFIG_DVB_USB_DTV5100=m +CONFIG_DVB_USB_FRIIO=m +CONFIG_DVB_USB_AZ6027=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_USB_V2=y +CONFIG_DVB_USB_AF9015=m +CONFIG_DVB_USB_AF9035=m +CONFIG_DVB_USB_ANYSEE=m +CONFIG_DVB_USB_AU6610=m +CONFIG_DVB_USB_AZ6007=m +CONFIG_DVB_USB_CE6230=m +CONFIG_DVB_USB_EC168=m +CONFIG_DVB_USB_GL861=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_USB_MXL111SF=m +CONFIG_DVB_USB_RTL28XXU=m +CONFIG_DVB_USB_DVBSKY=m +CONFIG_SMS_USB_DRV=m +CONFIG_DVB_B2C2_FLEXCOP_USB=m +CONFIG_DVB_B2C2_FLEXCOP_USB_DEBUG=y +CONFIG_DVB_AS102=m + +# +# Webcam, TV (analog/digital) USB devices +# +# CONFIG_VIDEO_EM28XX is not set + +# +# Software defined radio USB devices +# +CONFIG_USB_AIRSPY=m +CONFIG_USB_HACKRF=m +CONFIG_USB_MSI2500=m +CONFIG_V4L_PLATFORM_DRIVERS=y +# CONFIG_SOC_CAMERA is not set +# CONFIG_VIDEO_XILINX is not set +CONFIG_V4L_MEM2MEM_DRIVERS=y +# CONFIG_VIDEO_SH_VEU is not set +CONFIG_V4L_TEST_DRIVERS=y +CONFIG_VIDEO_VIVID=m +CONFIG_VIDEO_VIM2M=m +CONFIG_DVB_PLATFORM_DRIVERS=y +CONFIG_DVB_C8SECTPFE=m + +# +# Supported MMC/SDIO adapters +# +CONFIG_SMS_SDIO_DRV=m +CONFIG_RADIO_ADAPTERS=y +CONFIG_RADIO_TEA575X=m +CONFIG_RADIO_SI470X=y +CONFIG_USB_SI470X=m +CONFIG_I2C_SI470X=m +CONFIG_RADIO_SI4713=m +CONFIG_USB_SI4713=m +CONFIG_PLATFORM_SI4713=m +CONFIG_I2C_SI4713=m +CONFIG_USB_MR800=m +CONFIG_USB_DSBR=m +CONFIG_RADIO_SHARK=m +CONFIG_RADIO_SHARK2=m +CONFIG_USB_KEENE=m +CONFIG_USB_RAREMONO=m +CONFIG_USB_MA901=m +CONFIG_RADIO_TEA5764=m +CONFIG_RADIO_SAA7706H=m +CONFIG_RADIO_TEF6862=m +CONFIG_RADIO_WL1273=m + +# +# Texas Instruments WL128x FM driver (ST based) +# +CONFIG_MEDIA_COMMON_OPTIONS=y + +# +# common driver options +# +CONFIG_VIDEO_CX2341X=m +CONFIG_VIDEO_TVEEPROM=m +CONFIG_CYPRESS_FIRMWARE=m +CONFIG_DVB_B2C2_FLEXCOP=m +CONFIG_DVB_B2C2_FLEXCOP_DEBUG=y +CONFIG_SMS_SIANO_MDTV=m +CONFIG_SMS_SIANO_RC=y +CONFIG_SMS_SIANO_DEBUGFS=y + +# +# Media ancillary drivers (tuners, sensors, i2c, frontends) +# +# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set +CONFIG_MEDIA_ATTACH=y +CONFIG_VIDEO_IR_I2C=y + +# +# Encoders, decoders, sensors and other helper chips +# + +# +# Audio decoders, processors and mixers +# +CONFIG_VIDEO_TVAUDIO=m +CONFIG_VIDEO_TDA7432=m +CONFIG_VIDEO_TDA9840=m +CONFIG_VIDEO_TEA6415C=m +CONFIG_VIDEO_TEA6420=m +CONFIG_VIDEO_MSP3400=m +CONFIG_VIDEO_CS5345=m +CONFIG_VIDEO_CS53L32A=m +CONFIG_VIDEO_TLV320AIC23B=m +CONFIG_VIDEO_UDA1342=m +CONFIG_VIDEO_WM8775=m +CONFIG_VIDEO_WM8739=m +CONFIG_VIDEO_VP27SMPX=m +CONFIG_VIDEO_SONY_BTF_MPX=m + +# +# RDS decoders +# +CONFIG_VIDEO_SAA6588=m + +# +# Video decoders +# +CONFIG_VIDEO_ADV7180=m +CONFIG_VIDEO_ADV7183=m +CONFIG_VIDEO_ADV7604=m +CONFIG_VIDEO_ADV7842=m +CONFIG_VIDEO_BT819=m +CONFIG_VIDEO_BT856=m +CONFIG_VIDEO_BT866=m +CONFIG_VIDEO_KS0127=m +CONFIG_VIDEO_ML86V7667=m +CONFIG_VIDEO_SAA7110=m +CONFIG_VIDEO_SAA711X=m +CONFIG_VIDEO_TC358743=m +CONFIG_VIDEO_TVP514X=m +CONFIG_VIDEO_TVP5150=m +CONFIG_VIDEO_TVP7002=m +CONFIG_VIDEO_TW2804=m +CONFIG_VIDEO_TW9903=m +CONFIG_VIDEO_TW9906=m +CONFIG_VIDEO_VPX3220=m + +# +# Video and audio decoders +# +CONFIG_VIDEO_SAA717X=m +CONFIG_VIDEO_CX25840=m + +# +# Video encoders +# +CONFIG_VIDEO_SAA7127=m +CONFIG_VIDEO_SAA7185=m +CONFIG_VIDEO_ADV7170=m +CONFIG_VIDEO_ADV7175=m +CONFIG_VIDEO_ADV7343=m +CONFIG_VIDEO_ADV7393=m +CONFIG_VIDEO_ADV7511=m +CONFIG_VIDEO_AD9389B=m +CONFIG_VIDEO_AK881X=m +CONFIG_VIDEO_THS8200=m + +# +# Camera sensor devices +# +# CONFIG_VIDEO_OV2659 is not set +# CONFIG_VIDEO_OV7640 is not set +# CONFIG_VIDEO_OV7670 is not set +# CONFIG_VIDEO_OV9650 is not set +# CONFIG_VIDEO_VS6624 is not set +# CONFIG_VIDEO_MT9M032 is not set +# CONFIG_VIDEO_MT9P031 is not set +# CONFIG_VIDEO_MT9T001 is not set +# CONFIG_VIDEO_MT9V011 is not set +# CONFIG_VIDEO_MT9V032 is not set +# CONFIG_VIDEO_SR030PC30 is not set +# CONFIG_VIDEO_NOON010PC30 is not set +# CONFIG_VIDEO_M5MOLS is not set +# CONFIG_VIDEO_S5K6AA is not set +# CONFIG_VIDEO_S5K6A3 is not set +# CONFIG_VIDEO_S5K4ECGX is not set +# CONFIG_VIDEO_S5K5BAF is not set +# CONFIG_VIDEO_SMIAPP is not set +# CONFIG_VIDEO_S5C73M3 is not set + +# +# Flash devices +# +CONFIG_VIDEO_ADP1653=m +CONFIG_VIDEO_AS3645A=m +CONFIG_VIDEO_LM3560=m +CONFIG_VIDEO_LM3646=m + +# +# Video improvement chips +# +CONFIG_VIDEO_UPD64031A=m +CONFIG_VIDEO_UPD64083=m + +# +# Audio/Video compression chips +# +CONFIG_VIDEO_SAA6752HS=m + +# +# Miscellaneous helper chips +# +CONFIG_VIDEO_THS7303=m +CONFIG_VIDEO_M52790=m + +# +# Sensors used on soc_camera driver +# +CONFIG_MEDIA_TUNER=y + +# +# Customize TV tuners +# +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA827X=y +CONFIG_MEDIA_TUNER_TDA18271=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5761=m +CONFIG_MEDIA_TUNER_TEA5767=m +CONFIG_MEDIA_TUNER_MSI001=m +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_MT2060=m +CONFIG_MEDIA_TUNER_MT2063=m +CONFIG_MEDIA_TUNER_MT2266=m +CONFIG_MEDIA_TUNER_MT2131=m +CONFIG_MEDIA_TUNER_QT1010=m +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_MEDIA_TUNER_XC4000=y +CONFIG_MEDIA_TUNER_MXL5005S=m +CONFIG_MEDIA_TUNER_MXL5007T=m +CONFIG_MEDIA_TUNER_MC44S803=y +CONFIG_MEDIA_TUNER_MAX2165=m +CONFIG_MEDIA_TUNER_TDA18218=m +CONFIG_MEDIA_TUNER_FC0011=m +CONFIG_MEDIA_TUNER_FC0012=m +CONFIG_MEDIA_TUNER_FC0013=m +CONFIG_MEDIA_TUNER_TDA18212=m +CONFIG_MEDIA_TUNER_E4000=m +CONFIG_MEDIA_TUNER_FC2580=m +CONFIG_MEDIA_TUNER_M88RS6000T=m +CONFIG_MEDIA_TUNER_TUA9001=m +CONFIG_MEDIA_TUNER_SI2157=y +CONFIG_MEDIA_TUNER_IT913X=m +CONFIG_MEDIA_TUNER_R820T=m +CONFIG_MEDIA_TUNER_MXL301RF=m +CONFIG_MEDIA_TUNER_QM1D1C0042=m + +# +# Customise DVB Frontends +# + +# +# Multistandard (satellite) frontends +# +CONFIG_DVB_STB0899=m +CONFIG_DVB_STB6100=m +CONFIG_DVB_STV090x=m +CONFIG_DVB_STV6110x=m +CONFIG_DVB_M88DS3103=y + +# +# Multistandard (cable + terrestrial) frontends +# +CONFIG_DVB_DRXK=m +CONFIG_DVB_TDA18271C2DD=m +CONFIG_DVB_SI2165=m + +# +# DVB-S (satellite) frontends +# +CONFIG_DVB_CX24110=m +CONFIG_DVB_CX24123=m +CONFIG_DVB_MT312=m +CONFIG_DVB_ZL10036=m +CONFIG_DVB_ZL10039=m +CONFIG_DVB_S5H1420=m +CONFIG_DVB_STV0288=m +CONFIG_DVB_STB6000=m +CONFIG_DVB_STV0299=m +CONFIG_DVB_STV6110=m +CONFIG_DVB_STV0900=m +CONFIG_DVB_TDA8083=m +CONFIG_DVB_TDA10086=m +CONFIG_DVB_TDA8261=m +CONFIG_DVB_VES1X93=m +CONFIG_DVB_TUNER_ITD1000=m +CONFIG_DVB_TUNER_CX24113=m +CONFIG_DVB_TDA826X=m +CONFIG_DVB_TUA6100=m +CONFIG_DVB_CX24116=m +CONFIG_DVB_CX24117=m +CONFIG_DVB_CX24120=m +CONFIG_DVB_SI21XX=m +CONFIG_DVB_TS2020=y +CONFIG_DVB_DS3000=m +CONFIG_DVB_MB86A16=m +CONFIG_DVB_TDA10071=m + +# +# DVB-T (terrestrial) frontends +# +CONFIG_DVB_SP8870=m +CONFIG_DVB_SP887X=m +CONFIG_DVB_CX22700=m +CONFIG_DVB_CX22702=m +CONFIG_DVB_S5H1432=m +CONFIG_DVB_DRXD=m +CONFIG_DVB_L64781=m +CONFIG_DVB_TDA1004X=m +CONFIG_DVB_NXT6000=m +CONFIG_DVB_MT352=m +CONFIG_DVB_ZL10353=m +CONFIG_DVB_DIB3000MB=m +CONFIG_DVB_DIB3000MC=m +CONFIG_DVB_DIB7000M=m +CONFIG_DVB_DIB7000P=m +CONFIG_DVB_DIB9000=m +CONFIG_DVB_TDA10048=m +CONFIG_DVB_AF9013=m +CONFIG_DVB_EC100=m +CONFIG_DVB_HD29L2=m +CONFIG_DVB_STV0367=m +CONFIG_DVB_CXD2820R=m +CONFIG_DVB_CXD2841ER=m +CONFIG_DVB_RTL2830=m +CONFIG_DVB_RTL2832=m +CONFIG_DVB_RTL2832_SDR=m +CONFIG_DVB_SI2168=y +CONFIG_DVB_AS102_FE=m + +# +# DVB-C (cable) frontends +# +CONFIG_DVB_VES1820=m +CONFIG_DVB_TDA10021=m +CONFIG_DVB_TDA10023=m +CONFIG_DVB_STV0297=m + +# +# ATSC (North American/Korean Terrestrial/Cable DTV) frontends +# +CONFIG_DVB_NXT200X=m +CONFIG_DVB_OR51211=m +CONFIG_DVB_OR51132=m +CONFIG_DVB_BCM3510=m +CONFIG_DVB_LGDT330X=m +CONFIG_DVB_LGDT3305=m +CONFIG_DVB_LGDT3306A=m +CONFIG_DVB_LG2160=m +CONFIG_DVB_S5H1409=m +CONFIG_DVB_AU8522=m +CONFIG_DVB_AU8522_DTV=m +CONFIG_DVB_AU8522_V4L=m +CONFIG_DVB_S5H1411=m + +# +# ISDB-T (terrestrial) frontends +# +CONFIG_DVB_S921=m +CONFIG_DVB_DIB8000=m +CONFIG_DVB_MB86A20S=m + +# +# ISDB-S (satellite) & ISDB-T (terrestrial) frontends +# +CONFIG_DVB_TC90522=m + +# +# Digital terrestrial only tuners/PLL +# +CONFIG_DVB_PLL=m +CONFIG_DVB_TUNER_DIB0070=m +CONFIG_DVB_TUNER_DIB0090=m + +# +# SEC control devices for DVB-S +# +CONFIG_DVB_DRX39XYJ=m +CONFIG_DVB_LNBH25=m +CONFIG_DVB_LNBP21=m +CONFIG_DVB_LNBP22=m +CONFIG_DVB_ISL6405=m +CONFIG_DVB_ISL6421=m +CONFIG_DVB_ISL6423=m +CONFIG_DVB_A8293=m +CONFIG_DVB_SP2=y +CONFIG_DVB_LGS8GL5=m +CONFIG_DVB_LGS8GXX=m +CONFIG_DVB_ATBM8830=m +CONFIG_DVB_TDA665x=m +CONFIG_DVB_IX2505V=m +CONFIG_DVB_M88RS2000=m +CONFIG_DVB_AF9033=m +CONFIG_DVB_HORUS3A=m +CONFIG_DVB_ASCOT2E=m + +# +# Tools to develop new frontends +# +CONFIG_DVB_DUMMY_FE=m + +# +# Graphics support +# +# CONFIG_IMX_IPUV3_CORE is not set +# CONFIG_DRM is not set + +# +# Frame buffer Devices +# +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +CONFIG_FB_CMDLINE=y +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +CONFIG_FB_SYS_FILLRECT=m +CONFIG_FB_SYS_COPYAREA=m +CONFIG_FB_SYS_IMAGEBLIT=m +# CONFIG_FB_FOREIGN_ENDIAN is not set +CONFIG_FB_SYS_FOPS=m +CONFIG_FB_DEFERRED_IO=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +CONFIG_FB_BACKLIGHT=y +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_OPENCORES is not set +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_SMSCUFX is not set +# CONFIG_FB_UDL is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_BROADSHEET is not set +# CONFIG_FB_AUO_K190X is not set +CONFIG_FB_SIMPLE=y +# CONFIG_FB_SSD1307 is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=m +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_ILI922X is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +# CONFIG_LCD_PLATFORM is not set +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_LD9040 is not set +# CONFIG_LCD_AMS369FG06 is not set +# CONFIG_LCD_LMS501KF03 is not set +# CONFIG_LCD_HX8357 is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +CONFIG_BACKLIGHT_PM8941_WLED=m +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +# CONFIG_BACKLIGHT_LM3639 is not set +CONFIG_BACKLIGHT_SKY81452=m +# CONFIG_BACKLIGHT_GPIO is not set +# CONFIG_BACKLIGHT_LV5207LP is not set +# CONFIG_BACKLIGHT_BD6107 is not set +# CONFIG_VGASTATE is not set +CONFIG_HDMI=y + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +# CONFIG_LOGO is not set +CONFIG_SOUND=m +# CONFIG_SOUND_OSS_CORE is not set +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_PROC_FS=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +CONFIG_SND_VMASTER=y +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_AC97_CODEC=m +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_ALOOP is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_MTS64 is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +# CONFIG_SND_PORTMAN2X4 is not set +# CONFIG_SND_AC97_POWER_SAVE is not set + +# +# HD-Audio +# +CONFIG_SND_HDA_PREALLOC_SIZE=64 +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=m +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +# CONFIG_SND_USB_6FIRE is not set +# CONFIG_SND_USB_HIFACE is not set +# CONFIG_SND_BCD2000 is not set +CONFIG_SND_USB_LINE6=m +CONFIG_SND_USB_POD=m +CONFIG_SND_USB_PODHD=m +CONFIG_SND_USB_TONEPORT=m +CONFIG_SND_USB_VARIAX=m +# CONFIG_SND_SOC is not set +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=m + +# +# HID support +# +CONFIG_HID=y +# CONFIG_HID_BATTERY_STRENGTH is not set +# CONFIG_HIDRAW is not set +# CONFIG_UHID is not set +CONFIG_HID_GENERIC=y + +# +# Special HID drivers +# +CONFIG_HID_A4TECH=m +CONFIG_HID_ACRUX=m +CONFIG_HID_ACRUX_FF=y +CONFIG_HID_APPLE=m +CONFIG_HID_APPLEIR=m +CONFIG_HID_AUREAL=m +CONFIG_HID_BELKIN=m +CONFIG_HID_BETOP_FF=m +CONFIG_HID_CHERRY=m +CONFIG_HID_CHICONY=m +CONFIG_HID_PRODIKEYS=m +CONFIG_HID_CP2112=m +CONFIG_HID_CYPRESS=m +CONFIG_HID_DRAGONRISE=m +CONFIG_DRAGONRISE_FF=y +CONFIG_HID_EMS_FF=m +CONFIG_HID_ELECOM=m +CONFIG_HID_ELO=m +CONFIG_HID_EZKEY=m +CONFIG_HID_GEMBIRD=m +CONFIG_HID_HOLTEK=m +CONFIG_HOLTEK_FF=y +CONFIG_HID_GT683R=m +CONFIG_HID_KEYTOUCH=m +CONFIG_HID_KYE=m +CONFIG_HID_UCLOGIC=m +CONFIG_HID_WALTOP=m +CONFIG_HID_GYRATION=m +CONFIG_HID_ICADE=m +CONFIG_HID_TWINHAN=m +CONFIG_HID_KENSINGTON=m +CONFIG_HID_LCPOWER=m +CONFIG_HID_LENOVO=m +CONFIG_HID_LOGITECH=m +CONFIG_HID_LOGITECH_HIDPP=m +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_LOGIWHEELS_FF=y +CONFIG_HID_MAGICMOUSE=m +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MONTEREY=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_NTRIG=m +CONFIG_HID_ORTEK=m +CONFIG_HID_PANTHERLORD=m +CONFIG_PANTHERLORD_FF=y +CONFIG_HID_PENMOUNT=m +CONFIG_HID_PETALYNX=m +CONFIG_HID_PICOLCD=m +CONFIG_HID_PICOLCD_FB=y +CONFIG_HID_PICOLCD_BACKLIGHT=y +CONFIG_HID_PICOLCD_LCD=y +CONFIG_HID_PICOLCD_LEDS=y +# CONFIG_HID_PICOLCD_CIR is not set +CONFIG_HID_PLANTRONICS=y +CONFIG_HID_PRIMAX=m +CONFIG_HID_ROCCAT=m +CONFIG_HID_SAITEK=m +CONFIG_HID_SAMSUNG=m +CONFIG_HID_SONY=m +CONFIG_SONY_FF=y +CONFIG_HID_SPEEDLINK=m +CONFIG_HID_STEELSERIES=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_RMI=m +CONFIG_HID_GREENASIA=m +CONFIG_GREENASIA_FF=y +CONFIG_HID_SMARTJOYPLUS=m +CONFIG_SMARTJOYPLUS_FF=y +CONFIG_HID_TIVO=m +CONFIG_HID_TOPSEED=m +CONFIG_HID_THINGM=m +CONFIG_HID_THRUSTMASTER=m +CONFIG_THRUSTMASTER_FF=y +CONFIG_HID_WACOM=m +CONFIG_HID_WIIMOTE=m +CONFIG_HID_XINMO=m +CONFIG_HID_ZEROPLUS=m +CONFIG_ZEROPLUS_FF=y +CONFIG_HID_ZYDACRON=m +CONFIG_HID_SENSOR_HUB=m +CONFIG_HID_SENSOR_CUSTOM_SENSOR=m + +# +# USB HID support +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# I2C HID support +# +# CONFIG_I2C_HID is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SUPPORT=y +CONFIG_USB_COMMON=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEFAULT_PERSIST=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +# CONFIG_USB_OTG_FSM is not set +CONFIG_USB_ULPI_BUS=m +CONFIG_USB_MON=m +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_XHCI_HCD is not set +CONFIG_USB_EHCI_HCD=y +# CONFIG_USB_EHCI_ROOT_HUB_TT is not set +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_HCD_PLATFORM=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_FUSBH200_HCD is not set +# CONFIG_USB_FOTG210_HCD is not set +# CONFIG_USB_MAX3421_HCD is not set +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_OHCI_HCD_PLATFORM=y +# CONFIG_USB_U132_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HCD_BCMA is not set +# CONFIG_USB_HCD_SSB is not set +# CONFIG_USB_HCD_TEST_MODE is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +# CONFIG_USB_PRINTER is not set +CONFIG_USB_WDM=m +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_REALTEK is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_STORAGE_ENE_UB6250 is not set +CONFIG_USB_UAS=m + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USBIP_CORE is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_HOST=y + +# +# Platform Glue Layer +# +CONFIG_USB_MUSB_SUNXI=m + +# +# MUSB DMA mode +# +CONFIG_MUSB_PIO_ONLY=y +# CONFIG_USB_DWC3 is not set +# CONFIG_USB_DWC2 is not set +# CONFIG_USB_CHIPIDEA is not set +CONFIG_USB_ISP1760=m +CONFIG_USB_ISP1760_HCD=y +CONFIG_USB_ISP1760_HOST_ROLE=y + +# +# USB port drivers +# +# CONFIG_USB_USS720 is not set +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_SIMPLE=m +CONFIG_USB_SERIAL_AIRCABLE=m +CONFIG_USB_SERIAL_ARK3116=m +CONFIG_USB_SERIAL_BELKIN=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_F81232=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_IUU=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +CONFIG_USB_SERIAL_METRO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7715_PARPORT=y +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_MXUPORT=m +CONFIG_USB_SERIAL_NAVMAN=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OTI6858=m +CONFIG_USB_SERIAL_QCAUX=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SPCP8X5=m +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_USB_SERIAL_SYMBOL=m +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +CONFIG_USB_SERIAL_WWAN=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_USB_SERIAL_OMNINET=m +CONFIG_USB_SERIAL_OPTICON=m +CONFIG_USB_SERIAL_XSENS_MT=m +CONFIG_USB_SERIAL_WISHBONE=m +CONFIG_USB_SERIAL_SSU100=m +CONFIG_USB_SERIAL_QT2=m +CONFIG_USB_SERIAL_DEBUG=m + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +CONFIG_USB_FTDI_ELAN=y +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_EHSET_TEST_FIXTURE is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_YUREX is not set +CONFIG_USB_EZUSB_FX2=m +# CONFIG_USB_HSIC_USB3503 is not set +# CONFIG_USB_LINK_LAYER_TEST is not set +CONFIG_USB_CHAOSKEY=m +# CONFIG_USB_ATM is not set + +# +# USB Physical Layer drivers +# +CONFIG_USB_PHY=y +CONFIG_NOP_USB_XCEIV=y +# CONFIG_AM335X_PHY_USB is not set +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ISP1301 is not set +# CONFIG_USB_ULPI is not set +# CONFIG_USB_GADGET is not set +# CONFIG_USB_LED_TRIG is not set +# CONFIG_UWB is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_CLKGATE is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_MINORS=8 +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +# CONFIG_MMC_SDHCI is not set +# CONFIG_MMC_DW is not set +# CONFIG_MMC_VUB300 is not set +# CONFIG_MMC_USHC is not set +# CONFIG_MMC_USDHI6ROL0 is not set +CONFIG_MMC_SUNXI=y +CONFIG_MMC_MTK=m +# CONFIG_MEMSTICK is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_FLASH=m + +# +# LED drivers +# +# CONFIG_LEDS_AAT1290 is not set +# CONFIG_LEDS_BCM6328 is not set +# CONFIG_LEDS_BCM6358 is not set +# CONFIG_LEDS_LM3530 is not set +# CONFIG_LEDS_LM3642 is not set +# CONFIG_LEDS_PCA9532 is not set +CONFIG_LEDS_GPIO=y +# CONFIG_LEDS_LP3944 is not set +# CONFIG_LEDS_LP5521 is not set +# CONFIG_LEDS_LP5523 is not set +# CONFIG_LEDS_LP5562 is not set +# CONFIG_LEDS_LP8501 is not set +# CONFIG_LEDS_LP8860 is not set +# CONFIG_LEDS_PCA955X is not set +# CONFIG_LEDS_PCA963X is not set +# CONFIG_LEDS_DAC124S085 is not set +# CONFIG_LEDS_REGULATOR is not set +# CONFIG_LEDS_BD2802 is not set +# CONFIG_LEDS_LT3593 is not set +# CONFIG_LEDS_TCA6507 is not set +# CONFIG_LEDS_TLC591XX is not set +# CONFIG_LEDS_LM355x is not set +# CONFIG_LEDS_KTD2692 is not set + +# +# LED driver for blink(1) USB RGB LED is under Special HID drivers (HID_THINGM) +# +# CONFIG_LEDS_BLINKM is not set +CONFIG_LEDS_SYSCON=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +# CONFIG_LEDS_TRIGGER_ONESHOT is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y +# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +CONFIG_LEDS_TRIGGER_CPU=y +CONFIG_LEDS_TRIGGER_GPIO=m +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y + +# +# iptables trigger is under Netfilter config (LED target) +# +# CONFIG_LEDS_TRIGGER_TRANSIENT is not set +# CONFIG_LEDS_TRIGGER_CAMERA is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_EDAC_ATOMIC_SCRUB=y +CONFIG_EDAC_SUPPORT=y +# CONFIG_EDAC is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +CONFIG_RTC_SYSTOHC=y +CONFIG_RTC_SYSTOHC_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +# CONFIG_RTC_INTF_SYSFS is not set +# CONFIG_RTC_INTF_PROC is not set +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_ABB5ZES3=m +CONFIG_RTC_DRV_ABX80X=m +CONFIG_RTC_DRV_DS1307=m +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS3232 is not set +# CONFIG_RTC_DRV_HYM8563 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_ISL12022 is not set +# CONFIG_RTC_DRV_ISL12057 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF2127 is not set +# CONFIG_RTC_DRV_PCF8523 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF85063 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set +# CONFIG_RTC_DRV_EM3027 is not set +# CONFIG_RTC_DRV_RV3029C2 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T93 is not set +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1343 is not set +# CONFIG_RTC_DRV_DS1347 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set +# CONFIG_RTC_DRV_RX4581 is not set +# CONFIG_RTC_DRV_MCP795 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +CONFIG_RTC_DRV_DS1685_FAMILY=m +CONFIG_RTC_DRV_DS1685=y +# CONFIG_RTC_DRV_DS1689 is not set +# CONFIG_RTC_DRV_DS17285 is not set +# CONFIG_RTC_DRV_DS17485 is not set +# CONFIG_RTC_DRV_DS17885 is not set +# CONFIG_RTC_DS1685_PROC_REGS is not set +# CONFIG_RTC_DS1685_SYSFS_REGS is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_DS2404 is not set +CONFIG_RTC_DRV_DA9063=m +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set +CONFIG_RTC_DRV_ZYNQMP=m + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_SUN6I=y +CONFIG_RTC_DRV_SUNXI=y +# CONFIG_RTC_DRV_SNVS is not set +# CONFIG_RTC_DRV_MT6397 is not set + +# +# HID Sensor RTC drivers +# +# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set +# CONFIG_VIRT_DRIVERS is not set + +# +# Virtio drivers +# +# CONFIG_VIRTIO_MMIO is not set + +# +# Microsoft Hyper-V guest support +# +CONFIG_STAGING=y +# CONFIG_PRISM2_USB is not set +# CONFIG_COMEDI is not set +# CONFIG_PANEL is not set +# CONFIG_RTLLIB is not set +CONFIG_R8712U=m +CONFIG_R8188EU=m +CONFIG_88EU_AP_MODE=y +# CONFIG_R8723AU is not set +# CONFIG_VT6656 is not set +# CONFIG_FT1000 is not set + +# +# Speakup console speech +# +# CONFIG_SPEAKUP is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set +CONFIG_STAGING_MEDIA=y +CONFIG_I2C_BCM2048=m +CONFIG_DVB_MN88472=m +CONFIG_DVB_MN88473=m +# CONFIG_LIRC_STAGING is not set + +# +# Android +# +# CONFIG_STAGING_BOARD is not set +# CONFIG_WIMAX_GDM72XX is not set +# CONFIG_LTE_GDM724X is not set +# CONFIG_LUSTRE_FS is not set +# CONFIG_DGAP is not set +# CONFIG_GS_FPGABOOT is not set +# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set +CONFIG_FB_TFT=m +CONFIG_FB_TFT_AGM1264K_FL=m +CONFIG_FB_TFT_BD663474=m +CONFIG_FB_TFT_HX8340BN=m +CONFIG_FB_TFT_HX8347D=m +CONFIG_FB_TFT_HX8353D=m +CONFIG_FB_TFT_HX8357D=m +CONFIG_FB_TFT_ILI9163=m +CONFIG_FB_TFT_ILI9320=m +CONFIG_FB_TFT_ILI9325=m +CONFIG_FB_TFT_ILI9340=m +CONFIG_FB_TFT_ILI9341=m +CONFIG_FB_TFT_ILI9481=m +CONFIG_FB_TFT_ILI9486=m +CONFIG_FB_TFT_PCD8544=m +CONFIG_FB_TFT_RA8875=m +CONFIG_FB_TFT_S6D02A1=m +CONFIG_FB_TFT_S6D1121=m +CONFIG_FB_TFT_SSD1289=m +CONFIG_FB_TFT_SSD1306=m +CONFIG_FB_TFT_SSD1331=m +CONFIG_FB_TFT_SSD1351=m +CONFIG_FB_TFT_ST7735R=m +CONFIG_FB_TFT_TINYLCD=m +CONFIG_FB_TFT_TLS8204=m +CONFIG_FB_TFT_UC1611=m +CONFIG_FB_TFT_UC1701=m +CONFIG_FB_TFT_UPD161704=m +CONFIG_FB_TFT_WATTEROTT=m +CONFIG_FB_FLEX=m +CONFIG_FB_TFT_FBTFT_DEVICE=m +CONFIG_MOST=m +CONFIG_MOSTCORE=m +CONFIG_AIM_CDEV=m +CONFIG_AIM_NETWORK=m +CONFIG_AIM_SOUND=m +CONFIG_AIM_V4L2=m +CONFIG_HDM_DIM2=m +CONFIG_HDM_I2C=m +CONFIG_HDM_USB=m +# CONFIG_CHROME_PLATFORMS is not set +CONFIG_CLKDEV_LOOKUP=y +CONFIG_HAVE_CLK_PREPARE=y +CONFIG_COMMON_CLK=y + +# +# Common Clock Framework +# +# CONFIG_COMMON_CLK_SI5351 is not set +# CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_COMMON_CLK_CDCE925 is not set +CONFIG_CLK_QORIQ=y +# CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_CDCE706 is not set + +# +# Hardware Spinlock drivers +# + +# +# Clock Source drivers +# +CONFIG_CLKSRC_OF=y +CONFIG_CLKSRC_MMIO=y +CONFIG_SUN4I_TIMER=y +CONFIG_SUN5I_HSTIMER=y +CONFIG_ARM_ARCH_TIMER=y +CONFIG_ARM_ARCH_TIMER_EVTSTREAM=y +# CONFIG_ARM_TIMER_SP804 is not set +# CONFIG_ATMEL_PIT is not set +# CONFIG_SH_TIMER_CMT is not set +# CONFIG_SH_TIMER_MTU2 is not set +# CONFIG_SH_TIMER_TMU is not set +# CONFIG_EM_TIMER_STI is not set +# CONFIG_MAILBOX is not set +# CONFIG_IOMMU_SUPPORT is not set + +# +# Remoteproc drivers +# +# CONFIG_STE_MODEM_RPROC is not set + +# +# Rpmsg drivers +# + +# +# SOC (System On Chip) specific Drivers +# +CONFIG_SUNXI_RSB=y +CONFIG_SUNXI_SRAM=y +# CONFIG_SOC_TI is not set +# CONFIG_PM_DEVFREQ is not set +CONFIG_EXTCON=m + +# +# Extcon Device Drivers +# +# CONFIG_EXTCON_AXP288 is not set +CONFIG_EXTCON_GPIO=m +CONFIG_EXTCON_RT8973A=m +CONFIG_EXTCON_SM5502=m +CONFIG_EXTCON_USB_GPIO=m +# CONFIG_MEMORY is not set +# CONFIG_IIO is not set +# CONFIG_PWM is not set +CONFIG_IRQCHIP=y +CONFIG_ARM_GIC=y +# CONFIG_IPACK_BUS is not set +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_RESET_CONTROLLER=y +# CONFIG_FMC is not set + +# +# PHY Subsystem +# +CONFIG_GENERIC_PHY=y +# CONFIG_PHY_PXA_28NM_HSIC is not set +# CONFIG_PHY_PXA_28NM_USB2 is not set +# CONFIG_BCM_KONA_USB2_PHY is not set +CONFIG_PHY_SUN4I_USB=m +CONFIG_PHY_SUN9I_USB=y +# CONFIG_PHY_TUSB1210 is not set +# CONFIG_POWERCAP is not set +# CONFIG_MCB is not set + +# +# Performance monitor support +# +CONFIG_ARM_PMU=y +# CONFIG_RAS is not set + +# +# Android +# +# CONFIG_ANDROID is not set +# CONFIG_NVMEM is not set + +# +# Firmware Drivers +# +CONFIG_ARM_PSCI_FW=y +# CONFIG_FIRMWARE_MEMMAP is not set + +# +# File systems +# +CONFIG_DCACHE_WORD_ACCESS=y +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_USE_FOR_EXT2=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXT4_ENCRYPTION=y +CONFIG_EXT4_FS_ENCRYPTION=y +# CONFIG_EXT4_DEBUG is not set +CONFIG_JBD2=y +# CONFIG_JBD2_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=y +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +# CONFIG_REISERFS_FS_XATTR is not set +CONFIG_JFS_FS=y +CONFIG_JFS_POSIX_ACL=y +# CONFIG_JFS_SECURITY is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +CONFIG_XFS_FS=y +CONFIG_XFS_QUOTA=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_XFS_RT=y +# CONFIG_XFS_WARN is not set +# CONFIG_XFS_DEBUG is not set +# CONFIG_GFS2_FS is not set +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +# CONFIG_BTRFS_FS_CHECK_INTEGRITY is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_ASSERT is not set +# CONFIG_NILFS2_FS is not set +CONFIG_F2FS_FS=y +CONFIG_F2FS_STAT_FS=y +CONFIG_F2FS_FS_XATTR=y +CONFIG_F2FS_FS_POSIX_ACL=y +# CONFIG_F2FS_FS_SECURITY is not set +# CONFIG_F2FS_CHECK_FS is not set +# CONFIG_F2FS_FS_ENCRYPTION is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY_USER=y +CONFIG_FANOTIFY=y +# CONFIG_QUOTA is not set +# CONFIG_QUOTA_NETLINK_INTERFACE is not set +CONFIG_QUOTACTL=y +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=m +CONFIG_OVERLAY_FS=m + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +CONFIG_UDF_FS=y +CONFIG_UDF_NLS=y + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +CONFIG_NTFS_FS=m +# CONFIG_NTFS_DEBUG is not set +CONFIG_NTFS_RW=y + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +# CONFIG_PROC_CHILDREN is not set +CONFIG_KERNFS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_TMPFS_XATTR=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ECRYPT_FS is not set +CONFIG_HFS_FS=y +CONFIG_HFSPLUS_FS=y +CONFIG_HFSPLUS_FS_POSIX_ACL=y +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_LOGFS is not set +# CONFIG_CRAMFS is not set +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_FILE_CACHE=y +# CONFIG_SQUASHFS_FILE_DIRECT is not set +# CONFIG_SQUASHFS_DECOMP_SINGLE is not set +CONFIG_SQUASHFS_DECOMP_MULTI=y +# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set +CONFIG_SQUASHFS_XATTR=y +CONFIG_SQUASHFS_ZLIB=y +# CONFIG_SQUASHFS_LZ4 is not set +CONFIG_SQUASHFS_LZO=y +CONFIG_SQUASHFS_XZ=y +CONFIG_SQUASHFS_4K_DEVBLK_SIZE=y +CONFIG_SQUASHFS_EMBEDDED=y +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX6FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_PSTORE is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V2=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_SWAP=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFS_USE_LEGACY_DNS is not set +CONFIG_NFS_USE_KERNEL_DNS=y +CONFIG_NFSD=m +CONFIG_NFSD_V2_ACL=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +# CONFIG_NFSD_PNFS is not set +# CONFIG_NFSD_FAULT_INJECTION is not set +CONFIG_GRACE_PERIOD=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_SUNRPC_SWAP=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_SUNRPC_DEBUG is not set +# CONFIG_CEPH_FS is not set +CONFIG_CIFS=y +# CONFIG_CIFS_STATS is not set +# CONFIG_CIFS_WEAK_PW_HASH is not set +# CONFIG_CIFS_UPCALL is not set +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_CIFS_ACL=y +CONFIG_CIFS_DEBUG=y +# CONFIG_CIFS_DEBUG2 is not set +# CONFIG_CIFS_DFS_UPCALL is not set +# CONFIG_CIFS_SMB2 is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_MAC_ROMAN is not set +# CONFIG_NLS_MAC_CELTIC is not set +# CONFIG_NLS_MAC_CENTEURO is not set +# CONFIG_NLS_MAC_CROATIAN is not set +# CONFIG_NLS_MAC_CYRILLIC is not set +# CONFIG_NLS_MAC_GAELIC is not set +# CONFIG_NLS_MAC_GREEK is not set +# CONFIG_NLS_MAC_ICELAND is not set +# CONFIG_NLS_MAC_INUIT is not set +# CONFIG_NLS_MAC_ROMANIAN is not set +# CONFIG_NLS_MAC_TURKISH is not set +CONFIG_NLS_UTF8=y + +# +# Kernel hacking +# + +# +# printk and dmesg options +# +CONFIG_PRINTK_TIME=y +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_DYNAMIC_DEBUG is not set + +# +# Compile-time checks and compiler options +# +# CONFIG_DEBUG_INFO is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_READABLE_ASM is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_PAGE_OWNER is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_SECTION_MISMATCH is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_DEBUG_KERNEL=y + +# +# Memory Debugging +# +# CONFIG_PAGE_EXTENSION is not set +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_SLUB_STATS is not set +CONFIG_HAVE_DEBUG_KMEMLEAK=y +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_STACK_USAGE is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_PER_CPU_MAPS is not set +# CONFIG_DEBUG_HIGHMEM is not set +# CONFIG_DEBUG_SHIRQ is not set + +# +# Debug Lockups and Hangs +# +# CONFIG_LOCKUP_DETECTOR is not set +# CONFIG_DETECT_HUNG_TASK is not set +# CONFIG_PANIC_ON_OOPS is not set +CONFIG_PANIC_ON_OOPS_VALUE=0 +CONFIG_PANIC_TIMEOUT=0 +CONFIG_SCHED_DEBUG=y +CONFIG_SCHED_INFO=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_SCHED_STACK_END_CHECK is not set +# CONFIG_DEBUG_TIMEKEEPING is not set +# CONFIG_TIMER_STATS is not set + +# +# Lock Debugging (spinlocks, mutexes, etc...) +# +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_ATOMIC_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_LOCK_TORTURE_TEST is not set +# CONFIG_STACKTRACE is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_PI_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set + +# +# RCU Debugging +# +# CONFIG_PROVE_RCU is not set +# CONFIG_SPARSE_RCU_POINTER is not set +# CONFIG_TORTURE_TEST is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=21 +# CONFIG_RCU_TRACE is not set +# CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_NOTIFIER_ERROR_INJECTION is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_TRACING_SUPPORT=y +# CONFIG_FTRACE is not set + +# +# Runtime Testing +# +# CONFIG_LKDTM is not set +# CONFIG_TEST_LIST_SORT is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_RBTREE_TEST is not set +# CONFIG_INTERVAL_TREE_TEST is not set +# CONFIG_PERCPU_TEST is not set +# CONFIG_ATOMIC64_SELFTEST is not set +# CONFIG_ASYNC_RAID6_TEST is not set +CONFIG_TEST_HEXDUMP=m +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_TEST_KSTRTOX is not set +# CONFIG_TEST_RHASHTABLE is not set +# CONFIG_DMA_API_DEBUG is not set +# CONFIG_TEST_LKM is not set +# CONFIG_TEST_USER_COPY is not set +# CONFIG_TEST_BPF is not set +# CONFIG_TEST_FIRMWARE is not set +# CONFIG_TEST_UDELAY is not set +# CONFIG_MEMTEST is not set +# CONFIG_TEST_STATIC_KEYS is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +# CONFIG_ARM_PTDUMP is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_ARM_UNWIND=y +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_SUN9I_UART0 is not set +CONFIG_DEBUG_SUNXI_UART0=y +# CONFIG_DEBUG_SUNXI_UART1 is not set +# CONFIG_DEBUG_SUNXI_R_UART is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_DEBUG_SEMIHOSTING is not set +# CONFIG_DEBUG_LL_UART_8250 is not set +# CONFIG_DEBUG_LL_UART_PL01X is not set +CONFIG_DEBUG_LL_INCLUDE="debug/8250.S" +CONFIG_DEBUG_UART_8250=y +# CONFIG_DEBUG_UART_BCM63XX is not set +CONFIG_DEBUG_UART_PHYS=0x01c28000 +CONFIG_DEBUG_UART_VIRT=0xf1c28000 +CONFIG_DEBUG_UART_8250_SHIFT=2 +# CONFIG_DEBUG_UART_8250_WORD is not set +# CONFIG_DEBUG_UART_8250_FLOW_CONTROL is not set +CONFIG_DEBUG_UNCOMPRESS=y +CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h" +# CONFIG_EARLY_PRINTK is not set +# CONFIG_PID_IN_CONTEXTIDR is not set +# CONFIG_DEBUG_SET_MODULE_RONX is not set +# CONFIG_CORESIGHT is not set + +# +# Security options +# +CONFIG_KEYS=y +# CONFIG_PERSISTENT_KEYRINGS is not set +# CONFIG_BIG_KEYS is not set +CONFIG_ENCRYPTED_KEYS=y +# CONFIG_SECURITY_DMESG_RESTRICT is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_XOR_BLOCKS=y +CONFIG_ASYNC_CORE=m +CONFIG_ASYNC_MEMCPY=m +CONFIG_ASYNC_XOR=m +CONFIG_ASYNC_PQ=m +CONFIG_ASYNC_RAID6_RECOV=m +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_RNG_DEFAULT=y +CONFIG_CRYPTO_PCOMP2=y +CONFIG_CRYPTO_AKCIPHER2=y +# CONFIG_CRYPTO_RSA is not set +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_USER is not set +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +CONFIG_CRYPTO_GF128MUL=y +CONFIG_CRYPTO_NULL=y +CONFIG_CRYPTO_NULL2=y +# CONFIG_CRYPTO_PCRYPT is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_MCRYPTD is not set +CONFIG_CRYPTO_AUTHENC=y +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +CONFIG_CRYPTO_CCM=m +CONFIG_CRYPTO_GCM=m +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set +CONFIG_CRYPTO_SEQIV=y +CONFIG_CRYPTO_ECHAINIV=m + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_CTR=y +CONFIG_CRYPTO_CTS=y +CONFIG_CRYPTO_ECB=y +# CONFIG_CRYPTO_LRW is not set +CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=y + +# +# Hash modes +# +CONFIG_CRYPTO_CMAC=y +CONFIG_CRYPTO_HMAC=y +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +CONFIG_CRYPTO_CRC32C=y +# CONFIG_CRYPTO_CRC32 is not set +# CONFIG_CRYPTO_CRCT10DIF is not set +CONFIG_CRYPTO_GHASH=m +# CONFIG_CRYPTO_POLY1305 is not set +CONFIG_CRYPTO_MD4=y +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +CONFIG_CRYPTO_AES=y +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_ARC4=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_FCRYPT=m +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_CHACHA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y +# CONFIG_CRYPTO_842 is not set +# CONFIG_CRYPTO_LZ4 is not set +# CONFIG_CRYPTO_LZ4HC is not set + +# +# Random Number Generation +# +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_CRYPTO_DRBG_MENU=y +CONFIG_CRYPTO_DRBG_HMAC=y +# CONFIG_CRYPTO_DRBG_HASH is not set +# CONFIG_CRYPTO_DRBG_CTR is not set +CONFIG_CRYPTO_DRBG=y +CONFIG_CRYPTO_JITTERENTROPY=y +CONFIG_CRYPTO_USER_API=m +# CONFIG_CRYPTO_USER_API_HASH is not set +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set +CONFIG_CRYPTO_USER_API_RNG=m +# CONFIG_CRYPTO_USER_API_AEAD is not set +CONFIG_CRYPTO_HW=y +CONFIG_CRYPTO_DEV_SUN4I_SS=m +# CONFIG_ASYMMETRIC_KEY_TYPE is not set + +# +# Certificates for signature checking +# +# CONFIG_SYSTEM_TRUSTED_KEYRING is not set +CONFIG_ARM_CRYPTO=y +# CONFIG_CRYPTO_SHA1_ARM is not set +CONFIG_CRYPTO_SHA256_ARM=m +# CONFIG_CRYPTO_SHA512_ARM is not set +# CONFIG_CRYPTO_AES_ARM is not set +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_RAID6_PQ=y +CONFIG_BITREVERSE=y +CONFIG_HAVE_ARCH_BITREVERSE=y +CONFIG_GENERIC_STRNCPY_FROM_USER=y +CONFIG_GENERIC_STRNLEN_USER=y +CONFIG_GENERIC_NET_UTILS=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_IO=y +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +CONFIG_CRC_ITU_T=y +CONFIG_CRC32=y +# CONFIG_CRC32_SELFTEST is not set +CONFIG_CRC32_SLICEBY8=y +# CONFIG_CRC32_SLICEBY4 is not set +# CONFIG_CRC32_SARWATE is not set +# CONFIG_CRC32_BIT is not set +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +# CONFIG_CRC8 is not set +# CONFIG_AUDIT_ARCH_COMPAT_GENERIC is not set +# CONFIG_RANDOM32_SELFTEST is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_LZ4_COMPRESS=m +CONFIG_LZ4_DECOMPRESS=y +CONFIG_XZ_DEC=y +CONFIG_XZ_DEC_X86=y +CONFIG_XZ_DEC_POWERPC=y +CONFIG_XZ_DEC_IA64=y +CONFIG_XZ_DEC_ARM=y +CONFIG_XZ_DEC_ARMTHUMB=y +CONFIG_XZ_DEC_SPARC=y +CONFIG_XZ_DEC_BCJ=y +# CONFIG_XZ_DEC_TEST is not set +CONFIG_DECOMPRESS_GZIP=y +CONFIG_DECOMPRESS_BZIP2=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DECOMPRESS_XZ=y +CONFIG_DECOMPRESS_LZO=y +CONFIG_DECOMPRESS_LZ4=y +CONFIG_GENERIC_ALLOCATOR=y +CONFIG_TEXTSEARCH=y +CONFIG_TEXTSEARCH_KMP=m +CONFIG_TEXTSEARCH_BM=m +CONFIG_TEXTSEARCH_FSM=m +CONFIG_ASSOCIATIVE_ARRAY=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y +CONFIG_HAS_DMA=y +CONFIG_CPU_RMAP=y +CONFIG_DQL=y +CONFIG_GLOB=y +# CONFIG_GLOB_SELFTEST is not set +CONFIG_NLATTR=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +# CONFIG_CORDIC is not set +# CONFIG_DDR is not set +CONFIG_LIBFDT=y +CONFIG_OID_REGISTRY=y +CONFIG_FONT_SUPPORT=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_SG_SPLIT is not set +CONFIG_ARCH_HAS_SG_CHAIN=y +CONFIG_VIRTUALIZATION=y diff --git a/config/xorg.conf.cubox b/config/xorg.conf.cubox new file mode 100644 index 000000000..5befbd6b1 --- /dev/null +++ b/config/xorg.conf.cubox @@ -0,0 +1,14 @@ +Section "Device" + Identifier "i.MX Accelerated Framebuffer Device" + Driver "vivante" + Option "fbdev" "/dev/fb0" + Option "vivante_fbdev" "/dev/fb0" + Option "HWcursor" "false" +EndSection + +Section "ServerFlags" + Option "BlankTime" "0" + Option "StandbyTime" "0" + Option "SuspendTime" "0" + Option "OffTime" "0" +EndSection \ No newline at end of file diff --git a/patch/kernel/banana-default/fbtft_for_older.patch b/patch/kernel/banana-default/fbtft_for_older.patch new file mode 100644 index 000000000..55e63c1d9 --- /dev/null +++ b/patch/kernel/banana-default/fbtft_for_older.patch @@ -0,0 +1,9505 @@ +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/cubox-default/fbtft_drivers.patch b/patch/kernel/cubox-default/fbtft_drivers.patch new file mode 100644 index 000000000..8ecb55230 --- /dev/null +++ b/patch/kernel/cubox-default/fbtft_drivers.patch @@ -0,0 +1,12012 @@ +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 03bed11..48f1f71 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/host1x/Kconfig" +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 07905d0..14810e4 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-$(CONFIG_HDMI) += hdmi.o + obj-y += fb_notify.o +diff --git a/drivers/video/fbtft/Kconfig b/drivers/video/fbtft/Kconfig +new file mode 100644 +index 0000000..995a910 +--- /dev/null ++++ b/drivers/video/fbtft/Kconfig +@@ -0,0 +1,169 @@ ++menuconfig FB_TFT ++ tristate "Support for small TFT LCD display modules" ++ depends on FB && SPI && GPIOLIB ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ select FB_SYS_FOPS ++ select FB_DEFERRED_IO ++ select FB_BACKLIGHT ++ ++config FB_TFT_AGM1264K_FL ++ tristate "FB driver for the AGM1264K-FL LCD display" ++ depends on FB_TFT ++ help ++ Framebuffer support for the AGM1264K-FL LCD display (two Samsung KS0108 compatable chips) ++ ++config FB_TFT_BD663474 ++ tristate "FB driver for the BD663474 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for BD663474 ++ ++config FB_TFT_HX8340BN ++ tristate "FB driver for the HX8340BN LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8340BN ++ ++config FB_TFT_HX8347D ++ tristate "FB driver for the HX8347D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8347D ++ ++config FB_TFT_HX8353D ++ tristate "FB driver for the HX8353D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8353D ++ ++config FB_TFT_ILI9320 ++ tristate "FB driver for the ILI9320 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9320 ++ ++config FB_TFT_ILI9325 ++ tristate "FB driver for the ILI9325 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9325 ++ ++config FB_TFT_ILI9340 ++ tristate "FB driver for the ILI9340 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9340 ++ ++config FB_TFT_ILI9341 ++ tristate "FB driver for the ILI9341 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9341 ++ ++config FB_TFT_ILI9481 ++ tristate "FB driver for the ILI9481 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9481 ++ ++config FB_TFT_ILI9486 ++ tristate "FB driver for the ILI9486 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9486 ++ ++config FB_TFT_PCD8544 ++ tristate "FB driver for the PCD8544 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for PCD8544 ++ ++config FB_TFT_RA8875 ++ tristate "FB driver for the RA8875 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for RA8875 ++ ++config FB_TFT_S6D02A1 ++ tristate "FB driver for the S6D02A1 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D02A1 ++ ++config FB_TFT_S6D1121 ++ tristate "FB driver for the S6D1211 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D1121 ++ ++config FB_TFT_SSD1289 ++ tristate "FB driver for the SSD1289 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1289 ++ ++config FB_TFT_SSD1306 ++ tristate "FB driver for the SSD1306 OLED Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1306 ++ ++config FB_TFT_SSD1331 ++ tristate "FB driver for the SSD1331 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1331 ++ ++config FB_TFT_SSD1351 ++ tristate "FB driver for the SSD1351 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1351 ++ ++config FB_TFT_ST7735R ++ tristate "FB driver for the ST7735R LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ST7735R ++ ++config FB_TFT_TINYLCD ++ tristate "FB driver for tinylcd.com display" ++ depends on FB_TFT ++ help ++ Custom Framebuffer support for tinylcd.com display ++ ++config FB_TFT_TLS8204 ++ tristate "FB driver for the TLS8204 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TLS8204 ++ ++config FB_TFT_UC1701 ++ tristate "FB driver for the UC1701 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for UC1701 ++ ++config FB_TFT_UPD161704 ++ tristate "FB driver for the uPD161704 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for uPD161704 ++ ++config FB_TFT_WATTEROTT ++ tristate "FB driver for the WATTEROTT LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for WATTEROTT ++ ++config FB_FLEX ++ tristate "Generic FB driver for TFT LCD displays" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TFT LCD displays. ++ ++config FB_TFT_FBTFT_DEVICE ++ tristate "Module to for adding FBTFT devices" ++ depends on FB_TFT +diff --git a/drivers/video/fbtft/Makefile b/drivers/video/fbtft/Makefile +new file mode 100644 +index 0000000..71c755d +--- /dev/null ++++ b/drivers/video/fbtft/Makefile +@@ -0,0 +1,60 @@ ++ifneq ($(KERNELRELEASE),) ++# kbuild part of makefile ++ ++# Optionally, include config file to allow out of tree kernel modules build ++-include $(src)/.config ++ ++# Core module ++obj-$(CONFIG_FB_TFT) += fbtft.o ++fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o ++ ++# drivers ++obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o ++obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o ++obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o ++obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o ++obj-$(CONFIG_FB_TFT_HX8353D) += fb_hx8353d.o ++obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o ++obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o ++obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o ++obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o ++obj-$(CONFIG_FB_TFT_ILI9481) += fb_ili9481.o ++obj-$(CONFIG_FB_TFT_ILI9486) += fb_ili9486.o ++obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o ++obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o ++obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o ++obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o ++obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o ++obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o ++obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o ++obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o ++obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o ++obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o ++obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o ++obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o ++obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o ++obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o ++obj-$(CONFIG_FB_FLEX) += flexfb.o ++ ++# Device modules ++obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o ++ ++else ++# normal makefile ++KDIR ?= /lib/modules/`uname -r`/build ++ ++default: .config ++ $(MAKE) -C $(KDIR) M=$$PWD modules ++ ++.config: ++ grep config Kconfig | cut -d' ' -f2 | sed 's@^@CONFIG_@; s@$$@=m@' > .config ++ ++install: ++ $(MAKE) -C $(KDIR) M=$$PWD modules_install ++ ++ ++clean: ++ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions \ ++ modules.order Module.symvers ++ ++endif +diff --git a/drivers/video/fbtft/README b/drivers/video/fbtft/README +new file mode 100644 +index 0000000..9bebc98 +--- /dev/null ++++ b/drivers/video/fbtft/README +@@ -0,0 +1,37 @@ ++ FBTFT ++========= ++ ++2015-01-19 ++The FBTFT drivers are now in the Linux kernel staging tree: https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing ++Development in this github repo has ceased. ++ ++ ++Linux Framebuffer drivers for small TFT LCD display modules. ++The module 'fbtft' makes writing drivers for some of these displays very easy. ++ ++Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. ++ ++INSTALLATION ++ Download kernel sources ++ ++ From Linux 3.15 ++ cd drivers/video/fbdev ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" ++ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ ++ ++ Before Linux 3.15 ++ cd drivers/video ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" ++ Add to drivers/video/Makefile: obj-y += fbtft/ ++ ++ Enable driver(s) in menuconfig and build the kernel ++ ++ ++See wiki for more information: https://github.com/notro/fbtft/wiki ++ ++ ++Source: https://github.com/notro/fbtft/ +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +new file mode 100644 +index 0000000..621497f +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +@@ -0,0 +1,87 @@ ++/* ++ * Device Tree overlay for HY28A display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28a_pins: hy28a_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28a: hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28a_pins>; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts: hy28a-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28a>,"spi-max-frequency:0"; ++ rotate = <&hy28a>,"rotate:0"; ++ fps = <&hy28a>,"fps:0"; ++ debug = <&hy28a>,"debug:0"; ++ xohms = <&hy28a_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28a>,"reset-gpios:4", ++ <&hy28a_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28a>,"led-gpios:4", ++ <&hy28a_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +new file mode 100644 +index 0000000..f774c4a +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +@@ -0,0 +1,142 @@ ++/* ++ * Device Tree overlay for HY28b display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28b_pins: hy28b_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28b: hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28b_pins>; ++ ++ spi-max-frequency = <48000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts: hy28b-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28b>,"spi-max-frequency:0"; ++ rotate = <&hy28b>,"rotate:0"; ++ fps = <&hy28b>,"fps:0"; ++ debug = <&hy28b>,"debug:0"; ++ xohms = <&hy28b_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28b>,"reset-gpios:4", ++ <&hy28b_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28b>,"led-gpios:4", ++ <&hy28b_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +new file mode 100644 +index 0000000..c06fe12 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +@@ -0,0 +1,109 @@ ++/* ++ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mz61581_pins: mz61581_pins { ++ brcm,pins = <4 15 18 25>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ mz61581: mz61581@0{ ++ compatible = "samsung,s6d02a1"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mz61581_pins>; ++ ++ spi-max-frequency = <128000000>; ++ spi-cpol; ++ spi-cpha; ++ ++ width = <320>; ++ height = <480>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ ++ reset-gpios = <&gpio 15 0>; ++ dc-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 0>; ++ ++ init = <0x10000b0 00 ++ 0x1000011 ++ 0x20000ff ++ 0x10000b3 0x02 0x00 0x00 0x00 ++ 0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43 ++ 0x10000c1 0x08 0x16 0x08 0x08 ++ 0x10000c4 0x11 0x07 0x03 0x03 ++ 0x10000c6 0x00 ++ 0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00 ++ 0x1000035 0x00 ++ 0x1000036 0xa0 ++ 0x100003a 0x55 ++ 0x1000044 0x00 0x01 ++ 0x10000d0 0x07 0x07 0x1d 0x03 ++ 0x10000d1 0x03 0x30 0x10 ++ 0x10000d2 0x03 0x14 0x04 ++ 0x1000029 ++ 0x100002c>; ++ ++ /* This is a workaround to make sure the init sequence slows down and doesn't fail */ ++ debug = <3>; ++ }; ++ ++ mz61581_ts: mz61581_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <4 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 4 0>; ++ ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&mz61581>, "spi-max-frequency:0"; ++ rotate = <&mz61581>, "rotate:0"; ++ fps = <&mz61581>, "fps:0"; ++ debug = <&mz61581>, "debug:0"; ++ xohms = <&mz61581_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +new file mode 100644 +index 0000000..8cd6a95 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +@@ -0,0 +1,94 @@ ++/* ++ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ piscreen_pins: piscreen_pins { ++ brcm,pins = <17 25 24 22>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ piscreen: piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&piscreen_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ regwidth = <16>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ debug = <0>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000ff ++ 0x100003a 0x55 ++ 0x1000036 0x28 ++ 0x10000c2 0x44 ++ 0x10000c5 0x00 0x00 0x00 0x00 ++ 0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00 ++ 0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0x1000029>; ++ }; ++ ++ piscreen-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&piscreen>,"spi-max-frequency:0"; ++ rotate = <&piscreen>,"rotate:0"; ++ fps = <&piscreen>,"fps:0"; ++ debug = <&piscreen>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +new file mode 100644 +index 0000000..e8a9365 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +@@ -0,0 +1,115 @@ ++/* ++ * Device Tree overlay for pitft resistive by Adafruit ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pitft_pins: pitft_pins { ++ brcm,pins = <24 25>; ++ brcm,function = <0 1>; /* in out */ ++ brcm,pull = <2 0>; /* pullup none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pitft: pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pitft_pins>; ++ ++ spi-max-frequency = <16000000>; ++ rotate = <90>; ++ fps = <25>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ debug = <0>; ++ }; ++ ++ pitft_ts@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "st,stmpe610"; ++ reg = <1>; ++ ++ spi-max-frequency = <500000>; ++ irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */ ++ interrupts = <24 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ ++ stmpe_gpio: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ /* ++ * only GPIO2 is wired/available ++ * and it is wired to the backlight ++ */ ++ st,norequest-mask = <0x7b>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/soc"; ++ __overlay__ { ++ backlight { ++ compatible = "gpio-backlight"; ++ gpios = <&stmpe_gpio 2 0>; ++ default-on; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&pitft>,"spi-max-frequency:0"; ++ rotate = <&pitft>,"rotate:0"; ++ fps = <&pitft>,"fps:0"; ++ debug = <&pitft>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +new file mode 100644 +index 0000000..0578810 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +@@ -0,0 +1,81 @@ ++/* ++ * Device Tree overlay for rpi-display by Watterott ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_display_pins: rpi_display_pins { ++ brcm,pins = <18 23 24 25>; ++ brcm,function = <1 1 1 0>; /* out out out in */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rpidisplay: rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_display_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpidisplay_ts: rpi-display-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <25 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&rpidisplay>,"spi-max-frequency:0"; ++ rotate = <&rpidisplay>,"rotate:0"; ++ fps = <&rpidisplay>,"fps:0"; ++ debug = <&rpidisplay>,"debug:0"; ++ xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +new file mode 100644 +index 0000000..881d2eb +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +@@ -0,0 +1,181 @@ ++/* ++ * tinylcd 3.5" display ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ tinylcd35_pins: tinylcd35_pins { ++ brcm,pins = <25 24 18>; ++ brcm,function = <1>; /* out */ ++ }; ++ tinylcd35_ts_pins: tinylcd35_ts_pins { ++ brcm,pins = <5>; ++ brcm,function = <0>; /* in */ ++ }; ++ keypad_pins: keypad_pins { ++ brcm,pins = <4 17 22 23 27>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <1>; /* down */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ tinylcd35: tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tinylcd35_pins>, ++ <&tinylcd35_ts_pins>; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ fps = <20>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ ++ init = <0x10000B0 0x80 ++ 0x10000C0 0x0A 0x0A ++ 0x10000C1 0x01 0x01 ++ 0x10000C2 0x33 ++ 0x10000C5 0x00 0x42 0x80 ++ 0x10000B1 0xD0 0x11 ++ 0x10000B4 0x02 ++ 0x10000B6 0x00 0x22 0x3B ++ 0x10000B7 0x07 ++ 0x1000036 0x58 ++ 0x10000F0 0x36 0xA5 0xD3 ++ 0x10000E5 0x80 ++ 0x10000E5 0x01 ++ 0x10000B3 0x00 ++ 0x10000E5 0x00 ++ 0x10000F0 0x36 0xA5 0x53 ++ 0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00 ++ 0x100003A 0x55 ++ 0x1000011 ++ 0x2000001 ++ 0x1000029>; ++ }; ++ ++ tinylcd35_ts: tinylcd35_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <5 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 5 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pcf8563: pcf8563@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* ++ * Values for input event code is found under the ++ * 'Keys and buttons' heading in include/uapi/linux/input.h ++ */ ++ fragment@4 { ++ target-path = "/soc"; ++ __overlay__ { ++ keypad: keypad { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&keypad_pins>; ++ status = "disabled"; ++ autorepeat; ++ ++ button@17 { ++ label = "GPIO KEY_UP"; ++ linux,code = <103>; ++ gpios = <&gpio 17 0>; ++ }; ++ button@22 { ++ label = "GPIO KEY_DOWN"; ++ linux,code = <108>; ++ gpios = <&gpio 22 0>; ++ }; ++ button@27 { ++ label = "GPIO KEY_LEFT"; ++ linux,code = <105>; ++ gpios = <&gpio 27 0>; ++ }; ++ button@23 { ++ label = "GPIO KEY_RIGHT"; ++ linux,code = <106>; ++ gpios = <&gpio 23 0>; ++ }; ++ button@4 { ++ label = "GPIO KEY_ENTER"; ++ linux,code = <28>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&tinylcd35>,"spi-max-frequency:0"; ++ rotate = <&tinylcd35>,"rotate:0"; ++ fps = <&tinylcd35>,"fps:0"; ++ debug = <&tinylcd35>,"debug:0"; ++ touch = <&tinylcd35_ts>,"status"; ++ touchgpio = <&tinylcd35_ts_pins>,"brcm,pins:0", ++ <&tinylcd35_ts>,"interrupts:0", ++ <&tinylcd35_ts>,"pendown-gpio:4"; ++ xohms = <&tinylcd35_ts>,"ti,x-plate-ohms;0"; ++ rtc = <&i2c1>,"status", ++ <&pcf8563>,"status"; ++ keypad = <&keypad>,"status"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/rpi.dts b/drivers/video/fbtft/dts/rpi.dts +new file mode 100644 +index 0000000..6c3ab6e +--- /dev/null ++++ b/drivers/video/fbtft/dts/rpi.dts +@@ -0,0 +1,414 @@ ++ ++/* ++ * FBTFT Device Tree part for the Raspberry Pi ++ * Add this file to the end of the *rpi-b.dts file ++ * ++ * Please keep it sorted alphabetically on display name ++ * ++ * Notes: ++ * - use ads7846 instead of tsc2046 to get module autoloading ++ * - use polarity 1 to drive backlight initially low (off): ++ * led-gpios = <&gpio 18 1>; ++ */ ++ ++&spi0 { ++ /* this is provided here to make it easy to enable SPI when editing this file */ ++// status = "okay"; ++}; ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28A ++ * ++ */ ++&spi0 { ++ hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28B ++ * NOT TESTED ++ */ ++&spi0 { ++ hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * ITEAD ++ * ITDB02-2.8 ++ * ++ */ ++/ { ++ itdb28 { ++ compatible = "ilitek,ili9325"; ++ status = "disabled"; ++ ++ rotate = <0>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 17 0>; ++ dc-gpios = <&gpio 3 0>; ++ cs-gpios = <&gpio 27 0>; ++ wr-gpios = <&gpio 2 0>; ++ db-gpios = <&gpio 9 0>, ++ <&gpio 11 0>, ++ <&gpio 18 0>, ++ <&gpio 23 0>, ++ <&gpio 24 0>, ++ <&gpio 25 0>, ++ <&gpio 8 0>, ++ <&gpio 7 0>; ++ /* LED pin drives backlight directly. Use transistor (50mA) */ ++ /* led-gpios = <&gpio 4 1>; */ ++ debug = <0>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Watterott ++ * RPi-Display - 2.8" Touch-Display (320x240) ++ * ++ */ ++&spi0 { ++ rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpi-display_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 25>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Ozzmaker ++ * PiScreen - 3.5" TFT touchscreen (480x320) ++ * ++ */ ++&spi0 { ++ piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <20000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000FF ++ 0x100003A 0x55 ++ 0x1000036 0x28 ++ 0x10000C2 0x44 ++ 0x10000C5 0x00 0x00 0x00 0x00 ++ 0x10000E0 0x0F 0x1F 0x1C 0x0C 0x0F 0x08 0x48 0x98 0x37 0x0A 0x13 0x04 0x11 0x0D 0x00 ++ 0x10000E1 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000E2 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0X1000029>; ++ debug = <0>; ++ }; ++ ++ piscreen_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Adafruit ++ * PiTFT Mini Kit - 320x240 2.8" TFT+Touchscreen for Raspberry Pi ++ * ++ * Couldn't get touch controller to work ++ */ ++ ++&gpio { ++ stmpets_pins: stmpets_pins { ++ brcm,pins = <24>; ++ brcm,function = <0>; /* gpio in */ ++ brcm,pull = <2>; /* pull up */ ++ }; ++}; ++ ++&spi0 { ++ pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <90>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ ++ init = <0x1000001 ++ 0x2000005 ++ 0x1000028 ++ 0x10000EF 0x03 0x80 0x02 ++ 0x10000CF 0x00 0xC1 0x30 ++ 0x10000ED 0x64 0x03 0x12 0x81 ++ 0x10000E8 0x85 0x00 0x78 ++ 0x10000CB 0x39 0x2C 0x00 0x34 0x02 ++ 0x10000F7 0x20 ++ 0x10000EA 0x00 0x00 ++ 0x10000C0 0x23 ++ 0x10000C1 0x10 ++ 0x10000C5 0x3e 0x28 ++ 0x10000C7 0x86 ++ 0x100003A 0x55 ++ 0x10000B1 0x00 0x18 ++ 0x10000B6 0x08 0x82 0x27 ++ 0x10000F2 0x00 ++ 0x1000026 0x01 ++ 0x10000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00 ++ 0x10000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F ++ 0x1000011 ++ 0x2000064 ++ 0x1000029 ++ 0x2000014>; ++ debug = <0>; ++ }; ++/* ++Touchscreen didn't work. I guess it has something to do with it being an interrupt controller. ++I have never tried this before with DT and ARCH_BCM2708. ++On ARCH_BCM2835 it should be OK, since the GPIO controller acts as an interrupt controller as well. ++(stmpe_device can't be used from 3.16, because irq_base can't be set anymore) ++ ++[ 8.889056] irq: irq_create_mapping(0xc3008800, 0xc2) ++[ 8.895698] irq: -> using domain @c3008800 ++[ 8.900796] irq: -> existing mapping on virq 194 ++[ 8.925048] stmpe-spi spi0.1: stmpe610 detected, chip id: 0x811 ++[ 8.936650] irq: Added domain (null) ++[ 8.941780] irq: irq_create_mapping(0xc1dc1a20, 0x0) ++[ 8.949047] irq: -> using domain @c1dc1a20 ++[ 8.954421] irq: -> virq allocation failed ++[ 8.959926] irq: irq_create_mapping(0xc1dc1a20, 0x1) ++[ 8.967366] irq: -> using domain @c1dc1a20 ++[ 8.972870] irq: -> virq allocation failed ++*/ ++ pitft_ts@1 { ++ compatible = "st,stmpe610"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ status = "disabled"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&stmpets_pins>; ++ ++ spi-max-frequency = <500000>; ++ interrupts = <3 24>; ++ interrupt-parent = <&intc>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * NeoSec LLC ++ * 3.5 inch TFT Display (320x480) ++ * ++ */ ++&spi0 { ++ tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ tinylcd35@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 3>; ++ pendown-gpio = <&gpio 3 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; +diff --git a/drivers/video/fbtft/fb_agm1264k-fl.c b/drivers/video/fbtft/fb_agm1264k-fl.c +new file mode 100644 +index 0000000..7fe4fa0 +--- /dev/null ++++ b/drivers/video/fbtft/fb_agm1264k-fl.c +@@ -0,0 +1,462 @@ ++/* ++ * FB driver for Two KS0108 LCD controllers in AGM1264K-FL display ++ * ++ * Copyright (C) 2014 ololoshka2871 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++/* Uncomment text line to use negative image on display */ ++/*#define NEGATIVE*/ ++ ++#define WHITE 0xff ++#define BLACK 0 ++ ++#define DRVNAME "fb_agm1264k-fl" ++#define WIDTH 64 ++#define HEIGHT 64 ++#define TOTALWIDTH (WIDTH * 2) /* because 2 x ks0108 in one display */ ++#define FPS 20 ++ ++#define EPIN gpio.wr ++#define RS gpio.dc ++#define RW gpio.aux[2] ++#define CS0 gpio.aux[0] ++#define CS1 gpio.aux[1] ++ ++ ++/* diffusing error (“Floyd-Steinberg”) */ ++#define DIFFUSING_MATRIX_WIDTH 2 ++#define DIFFUSING_MATRIX_HEIGHT 2 ++ ++static const signed char ++diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = { ++ {-1, 3}, ++ {3, 2}, ++}; ++ ++static const unsigned char gamma_correction_table[] = { ++0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, ++1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, ++6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, ++13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, ++22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, ++33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, ++46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ++62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81, ++82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102, ++103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121, ++123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143, ++145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166, ++168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192, ++194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, ++221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248, ++251, 253, 255 ++}; ++ ++static int init_display(struct fbtft_par *par) ++{ ++ u8 i; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ for (i = 0; i < 2; ++i) { ++ write_reg(par, i, 0x3f); /* display on */ ++ write_reg(par, i, 0x40); /* set x to 0 */ ++ write_reg(par, i, 0xb0); /* set page to 0 */ ++ write_reg(par, i, 0xc0); /* set start line to 0 */ ++ } ++ ++ return 0; ++} ++ ++void reset(struct fbtft_par *par) ++{ ++ if (par->gpio.reset == -1) ++ return; ++ ++ fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__); ++ ++ gpio_set_value(par->gpio.reset, 0); ++ udelay(20); ++ gpio_set_value(par->gpio.reset, 1); ++ mdelay(120); ++} ++ ++/* Check if all necessary GPIOS defined */ ++static int verify_gpios(struct fbtft_par *par) ++{ ++ int i; ++ ++ fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device, ++ "%s()\n", __func__); ++ ++ if (par->EPIN < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'wr' (aka E) gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ for (i = 0; i < 8; ++i) { ++ if (par->gpio.db[i] < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'db[%i]' gpio. Aborting.\n", ++ i); ++ return -EINVAL; ++ } ++ } ++ if (par->CS0 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs0' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->CS1 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs1' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->RW < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'rw' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static unsigned long ++request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio) ++{ ++ fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device, ++ "%s('%s')\n", __func__, gpio->name); ++ ++ if (strcasecmp(gpio->name, "wr") == 0) { ++ /* left ks0108 E pin */ ++ par->EPIN = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } else if (strcasecmp(gpio->name, "cs0") == 0) { ++ /* left ks0108 controller pin */ ++ par->CS0 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } else if (strcasecmp(gpio->name, "cs1") == 0) { ++ /* right ks0108 controller pin */ ++ par->CS1 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } ++ ++ /* if write (rw = 0) e(1->0) perform write */ ++ /* if read (rw = 1) e(0->1) set data on D0-7*/ ++ else if (strcasecmp(gpio->name, "rw") == 0) { ++ par->RW = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } ++ ++ return FBTFT_GPIO_NO_MATCH; ++} ++ ++/* This function oses to enter commands ++ * first byte - destination controller 0 or 1 ++ * folowing - commands ++ */ ++static void write_reg8_bus8(struct fbtft_par *par, int len, ...) ++{ ++ va_list args; ++ int i, ret; ++ u8 *buf = (u8 *)par->buf; ++ ++ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { ++ va_start(args, len); ++ for (i = 0; i < len; i++) ++ buf[i] = (u8)va_arg(args, unsigned int); ++ ++ va_end(args); ++ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, ++ par->info->device, u8, buf, len, "%s: ", __func__); ++ } ++ ++ va_start(args, len); ++ ++ *buf = (u8)va_arg(args, unsigned int); ++ ++ if (*buf > 1) { ++ va_end(args); ++ dev_err(par->info->device, "%s: Incorrect chip sellect request (%d)\n", ++ __func__, *buf); ++ return; ++ } ++ ++ /* select chip */ ++ if (*buf) { ++ /* cs1 */ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 0); ++ } else { ++ /* cs0 */ ++ gpio_set_value(par->CS0, 0); ++ gpio_set_value(par->CS1, 1); ++ } ++ ++ gpio_set_value(par->RS, 0); /* RS->0 (command mode) */ ++ len--; ++ ++ if (len) { ++ i = len; ++ while (i--) ++ *buf++ = (u8)va_arg(args, unsigned int); ++ ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8))); ++ if (ret < 0) { ++ va_end(args); ++ dev_err(par->info->device, "%s: write() failed and returned %d\n", ++ __func__, ret); ++ return; ++ } ++ } ++ ++ va_end(args); ++} ++ ++static struct ++{ ++ int xs, ys_page, xe, ye_page; ++} addr_win; ++ ++/* save display writing zone */ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ addr_win.xs = xs; ++ addr_win.ys_page = ys / 8; ++ addr_win.xe = xe; ++ addr_win.ye_page = ye / 8; ++ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__, ++ addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page); ++} ++ ++static void ++construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src, ++ int xs, int xe, int y) ++{ ++ int x, i; ++ ++ for (x = xs; x < xe; ++x) { ++ u8 res = 0; ++ ++ for (i = 0; i < 8; i++) ++ if (src[(y * 8 + i) * par->info->var.xres + x]) ++ res |= 1 << i; ++#ifdef NEGATIVE ++ *dest++ = res; ++#else ++ *dest++ = ~res; ++#endif ++ } ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y; ++ int ret = 0; ++ ++ /* buffer to convert RGB565 -> grayscale16 -> Ditherd image 1bpp */ ++ signed short *convert_buf = kmalloc(par->info->var.xres * ++ par->info->var.yres * sizeof(signed short), GFP_NOIO); ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ /* converting to grayscale16 */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ u16 pixel = vmem16[y * par->info->var.xres + x]; ++ u16 b = pixel & 0x1f; ++ u16 g = (pixel & (0x3f << 5)) >> 5; ++ u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6); ++ ++ pixel = (299 * r + 587 * g + 114 * b) / 200; ++ if (pixel > 255) ++ pixel = 255; ++ ++ /* gamma-correction by table */ ++ convert_buf[y * par->info->var.xres + x] = ++ (signed short)gamma_correction_table[pixel]; ++ } ++ ++ /* Image Dithering */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ signed short pixel = ++ convert_buf[y * par->info->var.xres + x]; ++ signed short error_b = pixel - BLACK; ++ signed short error_w = pixel - WHITE; ++ signed short error; ++ u16 i, j; ++ ++ /* what color close? */ ++ if (abs(error_b) >= abs(error_w)) { ++ /* white */ ++ error = error_w; ++ pixel = 0xff; ++ } else { ++ /* black */ ++ error = error_b; ++ pixel = 0; ++ } ++ ++ error /= 8; ++ ++ /* diffusion matrix row */ ++ for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i) ++ /* diffusion matrix column */ ++ for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) { ++ signed short *write_pos; ++ signed char coeff; ++ ++ /* skip pixels out of zone */ ++ if (x + i < 0 || ++ x + i >= par->info->var.xres ++ || y + j >= par->info->var.yres) ++ continue; ++ write_pos = &convert_buf[ ++ (y + j) * par->info->var.xres + ++ x + i]; ++ coeff = diffusing_matrix[i][j]; ++ if (coeff == -1) ++ /* pixel itself */ ++ *write_pos = pixel; ++ else { ++ signed short p = *write_pos + ++ error * coeff; ++ ++ if (p > WHITE) ++ p = WHITE; ++ if (p < BLACK) ++ p = BLACK; ++ *write_pos = p; ++ } ++ } ++ } ++ ++ /* 1 string = 2 pages */ ++ for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) { ++ /* left half of display */ ++ if (addr_win.xs < par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, convert_buf, ++ addr_win.xs, par->info->var.xres / 2, y); ++ ++ len = par->info->var.xres / 2 - addr_win.xs; ++ ++ /* select left side (sc0) ++ * set addr ++ */ ++ write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs); ++ write_reg(par, 0x00, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ ret = par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ /* right half of display */ ++ if (addr_win.xe >= par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, ++ convert_buf, par->info->var.xres / 2, ++ addr_win.xe + 1, y); ++ ++ len = addr_win.xe + 1 - par->info->var.xres / 2; ++ ++ /* select right side (sc1) ++ * set addr ++ */ ++ write_reg(par, 0x01, (1 << 6)); ++ write_reg(par, 0x01, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ } ++ kfree(convert_buf); ++ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 1); ++ ++ return ret; ++} ++ ++static int write(struct fbtft_par *par, void *buf, size_t len) ++{ ++ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, ++ "%s(len=%d): ", __func__, len); ++ ++ gpio_set_value(par->RW, 0); /* set write mode */ ++ ++ ++ while (len--) { ++ u8 i, data; ++ ++ data = *(u8 *) buf++; ++ ++ /* set data bus */ ++ for (i = 0; i < 8; ++i) ++ gpio_set_value(par->gpio.db[i], data & (1 << i)); ++ /* set E */ ++ gpio_set_value(par->EPIN, 1); ++ udelay(5); ++ /* unset E - write */ ++ gpio_set_value(par->EPIN, 0); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = TOTALWIDTH, ++ .height = HEIGHT, ++ .fps = FPS, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .verify_gpios = verify_gpios, ++ .request_gpios_match = request_gpios_match, ++ .reset = reset, ++ .write = write, ++ .write_register = write_reg8_bus8, ++ .write_vmem = write_vmem, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display); ++ ++MODULE_ALIAS("platform:" DRVNAME); ++ ++MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display"); ++MODULE_AUTHOR("ololoshka2871"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_bd663474.c b/drivers/video/fbtft/fb_bd663474.c +new file mode 100644 +index 0000000..7e00c60 +--- /dev/null ++++ b/drivers/video/fbtft/fb_bd663474.c +@@ -0,0 +1,193 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "hitachi,bd663474", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:bd663474"); ++MODULE_ALIAS("platform:bd663474"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8340bn.c b/drivers/video/fbtft/fb_hx8340bn.c +new file mode 100644 +index 0000000..3939502 +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8340bn.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the HX8340BN LCD Controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8340bn", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8340bn"); ++MODULE_ALIAS("platform:hx8340bn"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8347d.c b/drivers/video/fbtft/fb_hx8347d.c +new file mode 100644 +index 0000000..8139a8f +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8347d.c +@@ -0,0 +1,181 @@ ++/* ++ * FB driver for the HX8347D LCD Controller ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * ++ * Based on driver code found here: https://github.com/watterott/r61505u-Adapter ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8347d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8347d"); ++MODULE_ALIAS("platform:hx8347d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8353d.c b/drivers/video/fbtft/fb_hx8353d.c +new file mode 100644 +index 0000000..c9512dc +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8353d.c +@@ -0,0 +1,166 @@ ++/* ++ * FB driver for the HX8353D LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8353d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8353d"); ++MODULE_ALIAS("platform:hx8353d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9320.c b/drivers/video/fbtft/fb_ili9320.c +new file mode 100644 +index 0000000..b26d893 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9320.c +@@ -0,0 +1,234 @@ ++/* ++ * FB driver for the ILI9320 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9320", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9320"); ++MODULE_ALIAS("platform:ili9320"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9325.c b/drivers/video/fbtft/fb_ili9325.c +new file mode 100644 +index 0000000..5f88145 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9325.c +@@ -0,0 +1,291 @@ ++/* ++ * FB driver for the ILI9325 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Based on ili9325.c by Jeroen Domburg ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9325", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9325"); ++MODULE_ALIAS("platform:ili9325"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9340.c b/drivers/video/fbtft/fb_ili9340.c +new file mode 100644 +index 0000000..985687d +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9340.c +@@ -0,0 +1,163 @@ ++/* ++ * FB driver for the ILI9340 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9340", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9340"); ++MODULE_ALIAS("platform:ili9340"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9341.c b/drivers/video/fbtft/fb_ili9341.c +new file mode 100644 +index 0000000..225b2d8 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9341.c +@@ -0,0 +1,179 @@ ++/* ++ * FB driver for the ILI9341 LCD display controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * Based on adafruit22fb.c by Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9341", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9341"); ++MODULE_ALIAS("platform:ili9341"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9481.c b/drivers/video/fbtft/fb_ili9481.c +new file mode 100644 +index 0000000..725157a +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9481.c +@@ -0,0 +1,117 @@ ++/* ++ * FB driver for the ILI9481 LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_ili9481" ++#define WIDTH 320 ++#define HEIGHT 480 ++ ++static int default_init_sequence[] = { ++ ++ /* SLP_OUT - Sleep out */ ++ -1, 0x11, ++ -2, 50, ++ /* Power setting */ ++ -1, 0xD0, 0x07, 0x42, 0x18, ++ /* VCOM */ ++ -1, 0xD1, 0x00, 0x07, 0x10, ++ /* Power setting for norm. mode */ ++ -1, 0xD2, 0x01, 0x02, ++ /* Panel driving setting */ ++ -1, 0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11, ++ /* Frame rate & inv. */ ++ -1, 0xC5, 0x03, ++ /* Pixel format */ ++ -1, 0x3A, 0x55, ++ /* Gamma */ ++ -1, 0xC8, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16, ++ 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00, ++ /* DISP_ON */ ++ -1, 0x29, ++ -3 ++}; ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* column address */ ++ write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff); ++ ++ /* row adress */ ++ write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff); ++ ++ /* memory write */ ++ write_reg(par, 0x2c); ++} ++ ++#define HFLIP 0x01 ++#define VFLIP 0x02 ++#define ROWxCOL 0x20 ++static int set_var(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ switch (par->info->var.rotate) { ++ case 270: ++ write_reg(par, 0x36, ROWxCOL | HFLIP | VFLIP | (par->bgr << 3)); ++ break; ++ case 180: ++ write_reg(par, 0x36, VFLIP | (par->bgr << 3)); ++ break; ++ case 90: ++ write_reg(par, 0x36, ROWxCOL | (par->bgr << 3)); ++ break; ++ default: ++ write_reg(par, 0x36, HFLIP | (par->bgr << 3)); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9481", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9481"); ++MODULE_ALIAS("platform:ili9481"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9481 LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9486.c b/drivers/video/fbtft/fb_ili9486.c +new file mode 100644 +index 0000000..95b8999 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9486.c +@@ -0,0 +1,121 @@ ++/* ++ * FB driver for the ILI9486 LCD Controller ++ * ++ * Copyright (C) 2014 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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 = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9486", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9486"); ++MODULE_ALIAS("platform:ili9486"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_pcd8544.c b/drivers/video/fbtft/fb_pcd8544.c +new file mode 100644 +index 0000000..678ab8e +--- /dev/null ++++ b/drivers/video/fbtft/fb_pcd8544.c +@@ -0,0 +1,177 @@ ++/* ++ * FB driver for the PCD8544 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "philips,pdc8544", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:pdc8544"); ++ ++MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ra8875.c b/drivers/video/fbtft/fb_ra8875.c +new file mode 100644 +index 0000000..c323c06 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ra8875.c +@@ -0,0 +1,331 @@ ++/****************************************************************************** ++ ++ 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, "raio,ra8875", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ra8875"); ++MODULE_ALIAS("platform:ra8875"); ++ ++MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller"); ++MODULE_AUTHOR("Pf@nne"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d02a1.c b/drivers/video/fbtft/fb_s6d02a1.c +new file mode 100644 +index 0000000..e412a42 +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d02a1.c +@@ -0,0 +1,168 @@ ++/* ++ * FB driver for the S6D02A1 LCD Controller ++ * ++ * Based on fb_st7735r.c by Noralf Tronnes ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d02a1", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d02a1"); ++MODULE_ALIAS("platform:s6d02a1"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller"); ++MODULE_AUTHOR("WOLFGANG BUENING"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d1121.c b/drivers/video/fbtft/fb_s6d1121.c +new file mode 100644 +index 0000000..1ef8c1a +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d1121.c +@@ -0,0 +1,208 @@ ++/* ++ * FB driver for the S6D1121 LCD Controller ++ * ++ * Copyright (C) 2013 Roman Rolinsky ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d1121", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d1121"); ++MODULE_ALIAS("platform:s6d1121"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller"); ++MODULE_AUTHOR("Roman Rolinsky"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1289.c b/drivers/video/fbtft/fb_ssd1289.c +new file mode 100644 +index 0000000..ef46fbc +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1289.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the SSD1289 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1289", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1289"); ++MODULE_ALIAS("platform:ssd1289"); ++ ++MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1306.c b/drivers/video/fbtft/fb_ssd1306.c +new file mode 100644 +index 0000000..5ea195b +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1306.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the SSD1306 OLED Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1306", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1306"); ++MODULE_ALIAS("platform:ssd1306"); ++ ++MODULE_DESCRIPTION("SSD1306 OLED Driver"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1331.c b/drivers/video/fbtft/fb_ssd1331.c +new file mode 100644 +index 0000000..da7464f +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1331.c +@@ -0,0 +1,205 @@ ++#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, "solomon,ssd1331", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1331"); ++MODULE_ALIAS("platform:ssd1331"); ++ ++MODULE_DESCRIPTION("SSD1331 OLED Driver"); ++MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1351.c b/drivers/video/fbtft/fb_ssd1351.c +new file mode 100644 +index 0000000..062d986 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1351.c +@@ -0,0 +1,258 @@ ++#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, "solomon,ssd1351", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1351"); ++MODULE_ALIAS("platform:ssd1351"); ++ ++MODULE_DESCRIPTION("SSD1351 OLED Driver"); ++MODULE_AUTHOR("James Davies"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_st7735r.c b/drivers/video/fbtft/fb_st7735r.c +new file mode 100644 +index 0000000..b63aa38 +--- /dev/null ++++ b/drivers/video/fbtft/fb_st7735r.c +@@ -0,0 +1,195 @@ ++/* ++ * FB driver for the ST7735R LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "sitronix,st7735r", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:st7735r"); ++MODULE_ALIAS("platform:st7735r"); ++ ++MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tinylcd.c b/drivers/video/fbtft/fb_tinylcd.c +new file mode 100644 +index 0000000..ca98bfb +--- /dev/null ++++ b/drivers/video/fbtft/fb_tinylcd.c +@@ -0,0 +1,124 @@ ++/* ++ * Custom FB driver for tinylcd.com display ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "neosec,tinylcd", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tinylcd"); ++ ++MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tls8204.c b/drivers/video/fbtft/fb_tls8204.c +new file mode 100644 +index 0000000..8738c7a +--- /dev/null ++++ b/drivers/video/fbtft/fb_tls8204.c +@@ -0,0 +1,176 @@ ++/* ++ * FB driver for the TLS8204 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * Copyright (C) 2014 Michael Hope (adapted for the TLS8204) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "teralane,tls8204", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tls8204"); ++ ++MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller"); ++MODULE_AUTHOR("Michael Hope"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_uc1701.c b/drivers/video/fbtft/fb_uc1701.c +new file mode 100644 +index 0000000..d70ac52 +--- /dev/null ++++ b/drivers/video/fbtft/fb_uc1701.c +@@ -0,0 +1,210 @@ ++/* ++ * FB driver for the UC1701 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2014 Juergen Holzmann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_uc1701" ++#define WIDTH 102 ++#define HEIGHT 64 ++#define PAGES (HEIGHT/8) ++ ++/* 1: Display on/off */ ++#define LCD_DISPLAY_ENABLE 0xAE ++/* 2: display start line set */ ++#define LCD_START_LINE 0x40 ++/* 3: Page address set (lower 4 bits select one of the pages) */ ++#define LCD_PAGE_ADDRESS 0xB0 ++/* 4: column address */ ++#define LCD_COL_ADDRESS 0x10 ++/* 8: select orientation */ ++#define LCD_BOTTOMVIEW 0xA0 ++/* 9: inverted display */ ++#define LCD_DISPLAY_INVERT 0xA6 ++/* 10: show memory content or switch all pixels on */ ++#define LCD_ALL_PIXEL 0xA4 ++/* 11: lcd bias set */ ++#define LCD_BIAS 0xA2 ++/* 14: Reset Controller */ ++#define LCD_RESET_CMD 0xE2 ++/* 15: output mode select (turns display upside-down) */ ++#define LCD_SCAN_DIR 0xC0 ++/* 16: power control set */ ++#define LCD_POWER_CONTROL 0x28 ++/* 17: voltage regulator resistor ratio set */ ++#define LCD_VOLTAGE 0x20 ++/* 18: Volume mode set */ ++#define LCD_VOLUME_MODE 0x81 ++/* 22: NOP command */ ++#define LCD_NO_OP 0xE3 ++/* 25: advanced program control */ ++#define LCD_ADV_PROG_CTRL 0xFA ++/* 25: advanced program control2 */ ++#define LCD_ADV_PROG_CTRL2 0x10 ++#define LCD_TEMPCOMP_HIGH 0x80 ++/* column offset for normal orientation */ ++#define SHIFT_ADDR_NORMAL 0 ++/* column offset for bottom view orientation */ ++#define SHIFT_ADDR_TOPVIEW 30 ++ ++ ++static int init_display(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ /* softreset of LCD */ ++ write_reg(par, LCD_RESET_CMD); ++ mdelay(10); ++ ++ /* set startpoint */ ++ /* LCD_START_LINE | (pos & 0x3F) */ ++ write_reg(par, LCD_START_LINE); ++ ++ /* select orientation BOTTOMVIEW */ ++ write_reg(par, LCD_BOTTOMVIEW | 1); ++ /* output mode select (turns display upside-down) */ ++ write_reg(par, LCD_SCAN_DIR | 0x00); ++ ++ /* Normal Pixel mode */ ++ write_reg(par, LCD_ALL_PIXEL | 0); ++ ++ /* positive display */ ++ write_reg(par, LCD_DISPLAY_INVERT | 0); ++ ++ /* bias 1/9 */ ++ write_reg(par, LCD_BIAS | 0); ++ ++ /* power control mode: all features on */ ++ /* LCD_POWER_CONTROL | (val&0x07) */ ++ write_reg(par, LCD_POWER_CONTROL | 0x07); ++ ++ /* set voltage regulator R/R */ ++ /* LCD_VOLTAGE | (val&0x07) */ ++ write_reg(par, LCD_VOLTAGE | 0x07); ++ ++ /* volume mode set */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_VOLUME_MODE); ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, 0x09); ++ /* ???? */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_NO_OP); ++ ++ /* advanced program control */ ++ write_reg(par, LCD_ADV_PROG_CTRL); ++ write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH); ++ ++ /* enable display */ ++ write_reg(par, LCD_DISPLAY_ENABLE | 1); ++ ++ return 0; ++} ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* goto address */ ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y, i; ++ int ret = 0; ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ for (y = 0; y < PAGES; y++) { ++ buf = par->txbuf.buf; ++ for (x = 0; x < WIDTH; x++) { ++ *buf = 0x00; ++ for (i = 0; i < 8; i++) ++ *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i; ++ buf++; ++ } ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS|(u8)y); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++ gpio_set_value(par->gpio.dc, 1); ++ ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); ++ gpio_set_value(par->gpio.dc, 0); ++ } ++ ++ if (ret < 0) ++ dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret); ++ ++ return ret; ++} ++ ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .write_vmem = write_vmem, ++ }, ++ .backlight = 1, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:uc1701"); ++ ++MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller"); ++MODULE_AUTHOR("Juergen Holzmann"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_upd161704.c b/drivers/video/fbtft/fb_upd161704.c +new file mode 100644 +index 0000000..fff57b3 +--- /dev/null ++++ b/drivers/video/fbtft/fb_upd161704.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "nec,upd161704", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:upd161704"); ++MODULE_ALIAS("platform:upd161704"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_watterott.c b/drivers/video/fbtft/fb_watterott.c +new file mode 100644 +index 0000000..975b579 +--- /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, "watterott,openlcd", &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..873e2c7 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft-core.c +@@ -0,0 +1,1516 @@ ++/* ++ * 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 ++#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 = true; ++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 ret; ++ ++ 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 = devm_gpio_request_one(par->info->device, ++ 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; ++} ++ ++#ifdef CONFIG_OF ++static int fbtft_request_one_gpio(struct fbtft_par *par, ++ const char *name, int index, int *gpiop) ++{ ++ struct device *dev = par->info->device; ++ struct device_node *node = dev->of_node; ++ int gpio, flags, ret = 0; ++ enum of_gpio_flags of_flags; ++ ++ if (of_find_property(node, name, NULL)) { ++ gpio = of_get_named_gpio_flags(node, name, index, &of_flags); ++ if (gpio == -ENOENT) ++ return 0; ++ if (gpio == -EPROBE_DEFER) ++ return gpio; ++ if (gpio < 0) { ++ dev_err(dev, ++ "failed to get '%s' from DT\n", name); ++ return gpio; ++ } ++ ++ /* active low translates to initially low */ ++ flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : ++ GPIOF_OUT_INIT_HIGH; ++ ret = devm_gpio_request_one(dev, gpio, flags, ++ dev->driver->name); ++ if (ret) { ++ dev_err(dev, ++ "gpio_request_one('%s'=%d) failed with %d\n", ++ name, gpio, ret); ++ return ret; ++ } ++ if (gpiop) ++ *gpiop = gpio; ++ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", ++ __func__, name, gpio); ++ } ++ ++ return ret; ++} ++ ++static int fbtft_request_gpios_dt(struct fbtft_par *par) ++{ ++ int i; ++ int ret; ++ ++ if (!par->info->device->of_node) ++ return -EINVAL; ++ ++ ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch); ++ if (ret) ++ return ret; ++ for (i = 0; i < 16; i++) { ++ ret = fbtft_request_one_gpio(par, "db-gpios", i, ++ &par->gpio.db[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "led-gpios", i, ++ &par->gpio.led[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "aux-gpios", i, ++ &par->gpio.aux[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#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->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; ++ int vmem_size, i; ++ 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; ++ ++ if (!pdata) { ++ dev_err(dev, "platform data is missing\n"); ++ return NULL; ++ } ++ ++ /* override driver values? */ ++ if (pdata->fps) ++ fps = pdata->fps; ++ if (pdata->txbuflen) ++ txbuflen = pdata->txbuflen; ++ 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; ++ 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; ++ if (pdata->display.regwidth) ++ display->regwidth = pdata->display.regwidth; ++ ++ display->debug |= debug; ++ fbtft_expand_debug_value(&display->debug); ++ ++ switch (pdata->rotate) { ++ case 90: ++ case 270: ++ width = display->height; ++ height = display->width; ++ break; ++ default: ++ width = display->width; ++ height = display->height; ++ } ++ ++ vmem_size = display->width * display->height * bpp / 8; ++ 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 = pdata->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 = pdata->bgr; ++ par->startbyte = pdata->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) { ++ if (fbtft_gamma_parse_str(par, ++ par->gamma.curves, gamma, strlen(gamma))) ++ goto alloc_fail; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ 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); ++ ++ 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); ++ ret = unregister_framebuffer(fb_info); ++ return ret; ++} ++EXPORT_SYMBOL(fbtft_unregister_framebuffer); ++ ++#ifdef CONFIG_OF ++/** ++ * fbtft_init_display_dt() - Device Tree init_display() function ++ * @par: Driver data ++ * ++ * Return: 0 if successful, negative if error ++ */ ++static int fbtft_init_display_dt(struct fbtft_par *par) ++{ ++ struct device_node *node = par->info->device->of_node; ++ struct property *prop; ++ const __be32 *p; ++ u32 val; ++ int buf[64], i, j; ++ char msg[128]; ++ char str[16]; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ if (!node) ++ return -EINVAL; ++ ++ prop = of_find_property(node, "init", NULL); ++ p = of_prop_next_u32(prop, NULL, &val); ++ if (!p) ++ return -EINVAL; ++ while (p) { ++ if (val & FBTFT_OF_INIT_CMD) { ++ val &= 0xFFFF; ++ i = 0; ++ while (p && !(val & 0xFFFF0000)) { ++ if (i > 63) { ++ dev_err(par->info->device, ++ "%s: Maximum register values exceeded\n", ++ __func__); ++ return -EINVAL; ++ } ++ buf[i++] = val; ++ p = of_prop_next_u32(prop, p, &val); ++ } ++ /* make debug message */ ++ msg[0] = '\0'; ++ for (j = 0; j < i; j++) { ++ snprintf(str, 128, " %02X", buf[j]); ++ strcat(msg, str); ++ } ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: write_register:%s\n", msg); ++ ++ par->fbtftops.write_register(par, i, ++ 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]); ++ } else if (val & FBTFT_OF_INIT_DELAY) { ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: msleep(%u)\n", val & 0xFFFF); ++ msleep(val & 0xFFFF); ++ p = of_prop_next_u32(prop, p, &val); ++ } else { ++ dev_err(par->info->device, "illegal init value 0x%X\n", ++ val); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++/** ++ * 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->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; ++} ++ ++#ifdef CONFIG_OF ++/* returns 0 if the property is not present */ ++static u32 fbtft_of_value(struct device_node *node, const char *propname) ++{ ++ int ret; ++ u32 val = 0; ++ ++ ret = of_property_read_u32(node, propname, &val); ++ if (ret == 0) ++ pr_info("%s: %s = %u\n", __func__, propname, val); ++ ++ return val; ++} ++ ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ struct fbtft_platform_data *pdata; ++ ++ if (!node) { ++ dev_err(dev, "Missing platform data or DT\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ pdata->display.width = fbtft_of_value(node, "width"); ++ pdata->display.height = fbtft_of_value(node, "height"); ++ pdata->display.regwidth = fbtft_of_value(node, "regwidth"); ++ pdata->display.buswidth = fbtft_of_value(node, "buswidth"); ++ pdata->display.backlight = fbtft_of_value(node, "backlight"); ++ pdata->display.bpp = fbtft_of_value(node, "bpp"); ++ pdata->display.debug = fbtft_of_value(node, "debug"); ++ pdata->rotate = fbtft_of_value(node, "rotate"); ++ pdata->bgr = of_property_read_bool(node, "bgr"); ++ pdata->fps = fbtft_of_value(node, "fps"); ++ pdata->txbuflen = fbtft_of_value(node, "txbuflen"); ++ pdata->startbyte = fbtft_of_value(node, "startbyte"); ++ of_property_read_string(node, "gamma", (const char **)&pdata->gamma); ++ ++ if (of_find_property(node, "led-gpios", NULL)) ++ pdata->display.backlight = 1; ++ if (of_find_property(node, "init", NULL)) ++ pdata->display.fbtftops.init_display = fbtft_init_display_dt; ++ pdata->display.fbtftops.request_gpios = fbtft_request_gpios_dt; ++ ++ return pdata; ++} ++#else ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ dev_err(dev, "Missing platform data\n"); ++ return ERR_PTR(-EINVAL); ++} ++#endif ++ ++/** ++ * 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) { ++ pdata = fbtft_probe_dt(dev); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ dev->platform_data = pdata; ++ } ++ ++ info = fbtft_framebuffer_alloc(display, dev); ++ if (!info) ++ return -ENOMEM; ++ ++ par = info->par; ++ par->spi = sdev; ++ par->pdev = pdev; ++ ++ if (display->buswidth == 0) { ++ dev_err(dev, "buswidth is not set\n"); ++ return -EINVAL; ++ } ++ ++ /* 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 */ ++ 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..45f8de3 +--- /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, 0660, show_gamma_curve, store_gamma_curve), ++}; ++ ++ ++void fbtft_expand_debug_value(unsigned long *debug) ++{ ++ switch (*debug & 0b111) { ++ case 1: ++ *debug |= DEBUG_LEVEL_1; ++ break; ++ case 2: ++ *debug |= DEBUG_LEVEL_2; ++ break; ++ case 3: ++ *debug |= DEBUG_LEVEL_3; ++ break; ++ case 4: ++ *debug |= DEBUG_LEVEL_4; ++ break; ++ case 5: ++ *debug |= DEBUG_LEVEL_5; ++ break; ++ case 6: ++ *debug |= DEBUG_LEVEL_6; ++ break; ++ case 7: ++ *debug = 0xFFFFFFFF; ++ break; ++ } ++} ++ ++static ssize_t store_debug(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ int ret; ++ ++ ret = kstrtoul(buf, 10, &par->debug); ++ if (ret) ++ return ret; ++ fbtft_expand_debug_value(&par->debug); ++ ++ return count; ++} ++ ++static ssize_t show_debug(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ ++ return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug); ++} ++ ++static struct device_attribute debug_device_attr = \ ++ __ATTR(debug, 0660, show_debug, store_debug); ++ ++ ++void fbtft_sysfs_init(struct fbtft_par *par) ++{ ++ device_create_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_create_file(par->info->dev, &gamma_device_attrs[0]); ++} ++ ++void fbtft_sysfs_exit(struct fbtft_par *par) ++{ ++ device_remove_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_remove_file(par->info->dev, &gamma_device_attrs[0]); ++} +diff --git a/drivers/video/fbtft/fbtft.h b/drivers/video/fbtft/fbtft.h +new file mode 100644 +index 0000000..0dbf3f9 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft.h +@@ -0,0 +1,447 @@ ++/* ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __LINUX_FBTFT_H ++#define __LINUX_FBTFT_H ++ ++#include ++#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 ++ ++#define FBTFT_OF_INIT_CMD BIT(24) ++#define FBTFT_OF_INIT_DELAY BIT(25) ++ ++/** ++ * struct fbtft_gpio - Structure that holds one pinname to gpio mapping ++ * @name: pinname (reset, dc, etc.) ++ * @gpio: GPIO number ++ * ++ */ ++struct fbtft_gpio { ++ char name[FBTFT_GPIO_NAME_SIZE]; ++ unsigned gpio; ++}; ++ ++struct fbtft_par; ++ ++/** ++ * struct fbtft_ops - FBTFT operations structure ++ * @write: Writes to interface bus ++ * @read: Reads from interface bus ++ * @write_vmem: Writes video memory to display ++ * @write_reg: Writes to controller register ++ * @set_addr_win: Set the GRAM update window ++ * @reset: Reset the LCD controller ++ * @mkdirty: Marks display lines for update ++ * @update_display: Updates the display ++ * @init_display: Initializes the display ++ * @blank: Blank the display (optional) ++ * @request_gpios_match: Do pinname to gpio matching ++ * @request_gpios: Request gpios from the kernel ++ * @free_gpios: Free previously requested gpios ++ * @verify_gpios: Verify that necessary gpios is present (optional) ++ * @register_backlight: Used to register backlight device (optional) ++ * @unregister_backlight: Unregister backlight device (optional) ++ * @set_var: Configure LCD with values from variables like @rotate and @bgr ++ * (optional) ++ * @set_gamma: Set Gamma curve (optional) ++ * ++ * Most of these operations have default functions assigned to them in ++ * fbtft_framebuffer_alloc() ++ */ ++struct fbtft_ops { ++ int (*write)(struct fbtft_par *par, void *buf, size_t len); ++ int (*read)(struct fbtft_par *par, void *buf, size_t len); ++ int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len); ++ void (*write_register)(struct fbtft_par *par, int len, ...); ++ ++ void (*set_addr_win)(struct fbtft_par *par, ++ int xs, int ys, int xe, int ye); ++ void (*reset)(struct fbtft_par *par); ++ void (*mkdirty)(struct fb_info *info, int from, int to); ++ void (*update_display)(struct fbtft_par *par, ++ unsigned start_line, unsigned end_line); ++ int (*init_display)(struct fbtft_par *par); ++ int (*blank)(struct fbtft_par *par, bool on); ++ ++ unsigned long (*request_gpios_match)(struct fbtft_par *par, ++ const struct fbtft_gpio *gpio); ++ int (*request_gpios)(struct fbtft_par *par); ++ int (*verify_gpios)(struct fbtft_par *par); ++ ++ void (*register_backlight)(struct fbtft_par *par); ++ void (*unregister_backlight)(struct fbtft_par *par); ++ ++ int (*set_var)(struct fbtft_par *par); ++ int (*set_gamma)(struct fbtft_par *par, unsigned long *curves); ++}; ++ ++/** ++ * struct fbtft_display - Describes the display properties ++ * @width: Width of display in pixels ++ * @height: Height of display in pixels ++ * @regwidth: LCD Controller Register width in bits ++ * @buswidth: Display interface bus width in bits ++ * @backlight: Backlight type. ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @bpp: Bits per pixel ++ * @fps: Frames per second ++ * @txbuflen: Size of transmit buffer ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma: String representation of Gamma curve(s) ++ * @gamma_num: Number of Gamma curves ++ * @gamma_len: Number of values per Gamma curve ++ * @debug: Initial debug value ++ * ++ * This structure is not stored by FBTFT except for init_sequence. ++ */ ++struct fbtft_display { ++ unsigned width; ++ unsigned height; ++ unsigned regwidth; ++ unsigned buswidth; ++ unsigned backlight; ++ struct fbtft_ops fbtftops; ++ unsigned bpp; ++ unsigned fps; ++ int txbuflen; ++ int *init_sequence; ++ char *gamma; ++ int gamma_num; ++ int gamma_len; ++ unsigned long debug; ++}; ++ ++/** ++ * struct fbtft_platform_data - Passes display specific data to the driver ++ * @display: Display properties ++ * @gpios: Pointer to an array of piname to gpio mappings ++ * @rotate: Display rotation angle ++ * @bgr: LCD Controller BGR bit ++ * @fps: Frames per second (this will go away, use @fps in @fbtft_display) ++ * @txbuflen: Size of transmit buffer ++ * @startbyte: When set, enables use of Startbyte in transfers ++ * @gamma: String representation of Gamma curve(s) ++ * @extra: A way to pass extra info ++ */ ++struct fbtft_platform_data { ++ struct fbtft_display display; ++ const struct fbtft_gpio *gpios; ++ unsigned rotate; ++ bool bgr; ++ unsigned fps; ++ int txbuflen; ++ u8 startbyte; ++ char *gamma; ++ void *extra; ++}; ++ ++/** ++ * struct fbtft_par - Main FBTFT data structure ++ * ++ * This structure holds all relevant data to operate the display ++ * ++ * See sourcefile for documentation since nested structs is not ++ * supported by kernel-doc. ++ * ++ */ ++/* @spi: Set if it is a SPI device ++ * @pdev: Set if it is a platform device ++ * @info: Pointer to framebuffer fb_info structure ++ * @pdata: Pointer to platform data ++ * @ssbuf: Not used ++ * @pseudo_palette: Used by fb_set_colreg() ++ * @txbuf.buf: Transmit buffer ++ * @txbuf.len: Transmit buffer length ++ * @buf: Small buffer used when writing init data over SPI ++ * @startbyte: Used by some controllers when in SPI mode. ++ * Format: 6 bit Device id + RS bit + RW bit ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @dirty_lock: Protects dirty_lines_start and dirty_lines_end ++ * @dirty_lines_start: Where to begin updating display ++ * @dirty_lines_end: Where to end updating display ++ * @gpio.reset: GPIO used to reset display ++ * @gpio.dc: Data/Command signal, also known as RS ++ * @gpio.rd: Read latching signal ++ * @gpio.wr: Write latching signal ++ * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch ++ * @gpio.cs: LCD Chip Select with parallel interface bus ++ * @gpio.db[16]: Parallel databus ++ * @gpio.led[16]: Led control signals ++ * @gpio.aux[16]: Auxillary signals, not used by core ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma.lock: Mutex for Gamma curve locking ++ * @gamma.curves: Pointer to Gamma curve array ++ * @gamma.num_values: Number of values per Gamma curve ++ * @gamma.num_curves: Number of Gamma curves ++ * @debug: Pointer to debug value ++ * @current_debug: ++ * @first_update_done: Used to only time the first display update ++ * @update_time: Used to calculate 'fps' in debug output ++ * @bgr: BGR mode/\n ++ * @extra: Extra info needed by driver ++ */ ++struct fbtft_par { ++ struct spi_device *spi; ++ struct platform_device *pdev; ++ struct fb_info *info; ++ struct fbtft_platform_data *pdata; ++ u16 *ssbuf; ++ u32 pseudo_palette[16]; ++ struct { ++ void *buf; ++ dma_addr_t dma; ++ size_t len; ++ } txbuf; ++ u8 *buf; ++ u8 startbyte; ++ struct fbtft_ops fbtftops; ++ spinlock_t dirty_lock; ++ unsigned dirty_lines_start; ++ unsigned dirty_lines_end; ++ struct { ++ int reset; ++ int dc; ++ int rd; ++ int wr; ++ int latch; ++ int cs; ++ int db[16]; ++ int led[16]; ++ int aux[16]; ++ } gpio; ++ int *init_sequence; ++ struct { ++ struct mutex lock; ++ unsigned long *curves; ++ int num_values; ++ int num_curves; ++ } gamma; ++ unsigned long debug; ++ bool first_update_done; ++ struct timespec update_time; ++ bool bgr; ++ void *extra; ++}; ++ ++#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) ++ ++#define write_reg(par, ...) \ ++do { \ ++ par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \ ++} while (0) ++ ++/* fbtft-core.c */ ++extern void fbtft_dbg_hex(const struct device *dev, ++ int groupsize, void *buf, size_t len, const char *fmt, ...); ++extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, ++ struct device *dev); ++extern void fbtft_framebuffer_release(struct fb_info *info); ++extern int fbtft_register_framebuffer(struct fb_info *fb_info); ++extern int fbtft_unregister_framebuffer(struct fb_info *fb_info); ++extern void fbtft_register_backlight(struct fbtft_par *par); ++extern void fbtft_unregister_backlight(struct fbtft_par *par); ++extern int fbtft_init_display(struct fbtft_par *par); ++extern int fbtft_probe_common(struct fbtft_display *display, ++ struct spi_device *sdev, struct platform_device *pdev); ++extern int fbtft_remove_common(struct device *dev, struct fb_info *info); ++ ++/* fbtft-io.c */ ++extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_spi_emulate_9(struct fbtft_par *par, ++ void *buf, size_t len); ++extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, ++ void *buf, size_t len); ++ ++/* fbtft-bus.c */ ++extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len); ++extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...); ++ ++ ++#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \ ++ \ ++static int fbtft_driver_probe_spi(struct spi_device *spi) \ ++{ \ ++ return fbtft_probe_common(_display, spi, NULL); \ ++} \ ++ \ ++static int fbtft_driver_remove_spi(struct spi_device *spi) \ ++{ \ ++ struct fb_info *info = spi_get_drvdata(spi); \ ++ \ ++ return fbtft_remove_common(&spi->dev, info); \ ++} \ ++ \ ++static int fbtft_driver_probe_pdev(struct platform_device *pdev) \ ++{ \ ++ return fbtft_probe_common(_display, NULL, pdev); \ ++} \ ++ \ ++static int fbtft_driver_remove_pdev(struct platform_device *pdev) \ ++{ \ ++ struct fb_info *info = platform_get_drvdata(pdev); \ ++ \ ++ return fbtft_remove_common(&pdev->dev, info); \ ++} \ ++ \ ++static const struct of_device_id dt_ids[] = { \ ++ { .compatible = _compatible }, \ ++ {}, \ ++}; \ ++ \ ++MODULE_DEVICE_TABLE(of, dt_ids); \ ++ \ ++ \ ++static struct spi_driver fbtft_driver_spi_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_spi, \ ++ .remove = fbtft_driver_remove_spi, \ ++}; \ ++ \ ++static struct platform_driver fbtft_driver_platform_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_pdev, \ ++ .remove = fbtft_driver_remove_pdev, \ ++}; \ ++ \ ++static int __init fbtft_driver_module_init(void) \ ++{ \ ++ int ret; \ ++ \ ++ ret = spi_register_driver(&fbtft_driver_spi_driver); \ ++ if (ret < 0) \ ++ return ret; \ ++ return platform_driver_register(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++static void __exit fbtft_driver_module_exit(void) \ ++{ \ ++ spi_unregister_driver(&fbtft_driver_spi_driver); \ ++ platform_driver_unregister(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++module_init(fbtft_driver_module_init); \ ++module_exit(fbtft_driver_module_exit); ++ ++ ++/* Debug macros */ ++ ++/* shorthand debug levels */ ++#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS ++#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE) ++#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS) ++#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK) ++#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY) ++#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5) ++#define DEBUG_LEVEL_7 0xFFFFFFFF ++ ++#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3) ++#define DEBUG_TIME_FIRST_UPDATE (1<<4) ++#define DEBUG_TIME_EACH_UPDATE (1<<5) ++#define DEBUG_DEFERRED_IO (1<<6) ++#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7) ++ ++/* fbops */ ++#define DEBUG_FB_READ (1<<8) ++#define DEBUG_FB_WRITE (1<<9) ++#define DEBUG_FB_FILLRECT (1<<10) ++#define DEBUG_FB_COPYAREA (1<<11) ++#define DEBUG_FB_IMAGEBLIT (1<<12) ++#define DEBUG_FB_SETCOLREG (1<<13) ++#define DEBUG_FB_BLANK (1<<14) ++ ++#define DEBUG_SYSFS (1<<16) ++ ++/* fbtftops */ ++#define DEBUG_BACKLIGHT (1<<17) ++#define DEBUG_READ (1<<18) ++#define DEBUG_WRITE (1<<19) ++#define DEBUG_WRITE_VMEM (1<<20) ++#define DEBUG_WRITE_REGISTER (1<<21) ++#define DEBUG_SET_ADDR_WIN (1<<22) ++#define DEBUG_RESET (1<<23) ++#define DEBUG_MKDIRTY (1<<24) ++#define DEBUG_UPDATE_DISPLAY (1<<25) ++#define DEBUG_INIT_DISPLAY (1<<26) ++#define DEBUG_BLANK (1<<27) ++#define DEBUG_REQUEST_GPIOS (1<<28) ++#define DEBUG_FREE_GPIOS (1<<29) ++#define DEBUG_REQUEST_GPIOS_MATCH (1<<30) ++#define DEBUG_VERIFY_GPIOS (1<<31) ++ ++ ++#define fbtft_init_dbg(dev, format, arg...) \ ++do { \ ++ if (unlikely((dev)->platform_data && \ ++ (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg(level, par, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(par->info->device, format, ##arg); \ ++} while (0) ++ ++#define fbtft_dev_dbg(level, par, dev, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \ ++} while (0) ++ ++#endif /* __LINUX_FBTFT_H */ +diff --git a/drivers/video/fbtft/fbtft_device.c b/drivers/video/fbtft/fbtft_device.c +new file mode 100644 +index 0000000..b9f4c30 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft_device.c +@@ -0,0 +1,1444 @@ ++/* ++ * ++ * 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 }; ++ ++static int waveshare32b_init_sequence[] = { ++ -1,0xCB,0x39,0x2C,0x00,0x34,0x02,-1,0xCF,0x00,0xC1,0x30, ++ -1,0xE8,0x85,0x00,0x78,-1,0xEA,0x00,0x00,-1,0xED,0x64,0x03,0x12,0x81, ++ -1,0xF7,0x20,-1,0xC0,0x23,-1,0xC1,0x10,-1,0xC5,0x3e,0x28,-1,0xC7,0x86, ++ -1,0x36,0x28,-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,120,-1,0x29,-1,0x2c,-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 = "adafruit28", ++ .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 = "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 = "agm1264k-fl", ++ .pdev = &(struct platform_device) { ++ .name = "fb_agm1264k-fl", ++ .id = 0, ++ .dev = { ++ .release = fbtft_device_pdev_release, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = FBTFT_ONBOARD_BACKLIGHT, ++ }, ++ .gpios = (const struct fbtft_gpio []) { ++ {}, ++ }, ++ }, ++ } ++ } ++ }, { ++ .name = "dogs102", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_uc1701", ++ .max_speed_hz = 8000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 13 }, ++ { "dc", 6 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = "ili9481", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 32000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .regwidth = 16, ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 25 }, ++ { "dc", 24 }, ++ { "led", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = { ++ .regwidth = 16, ++ .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 = "tontec35_9481", /* boards before 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "led_", 18 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .name = "tontec35_9486", /* boards after 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9486", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "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 = "waveshare32b", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9340", ++ .max_speed_hz = 48000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ .init_sequence = waveshare32b_init_sequence, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 27 }, ++ { "dc", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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/cubox-default/patch-3.14.19-20.patch b/patch/kernel/cubox-default/patch-3.14.19-20.patch index 863b597c8..e3560ed49 100644 --- a/patch/kernel/cubox-default/patch-3.14.19-20.patch +++ b/patch/kernel/cubox-default/patch-3.14.19-20.patch @@ -5486,15 +5486,6 @@ index 9da566a3f5c8..e47aabe0c760 100644 { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -@@ -1917,6 +1940,8 @@ static void option_instat_callback(struct urb *urb) - dev_dbg(dev, "%s: type %x req %x\n", __func__, - req_pkt->bRequestType, req_pkt->bRequest); - } -+ } else if (status == -ENOENT || status == -ESHUTDOWN) { -+ dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status); - } else - dev_err(dev, "%s: error %d\n", __func__, status); - diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index b3d5a35c0d4b..e9bad928039f 100644 --- a/drivers/usb/serial/pl2303.c diff --git a/patch/kernel/cubox-default/patch-3.14.30-31.patch b/patch/kernel/cubox-default/patch-3.14.30-31.patch index c557e2164..5fe953e2e 100644 --- a/patch/kernel/cubox-default/patch-3.14.30-31.patch +++ b/patch/kernel/cubox-default/patch-3.14.30-31.patch @@ -2412,19 +2412,6 @@ index 4913c0690872..175584ad643f 100644 /* we would like to get this block, possibly by computing it, * otherwise read it if the backing disk is insync */ -diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c -index 7e0176321aff..881bf89acfcc 100644 ---- a/drivers/mmc/host/sdhci.c -+++ b/drivers/mmc/host/sdhci.c -@@ -2537,7 +2537,7 @@ out: - /* - * We have to delay this as it calls back into the driver. - */ -- if (cardint) -+ if (cardint && host->mmc->sdio_irqs) - mmc_signal_sdio_irq(host->mmc); - - return result; diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index cc11f7f5e91d..1468c4658804 100644 --- a/drivers/net/can/dev.c diff --git a/patch/kernel/cubox-default/patch-3.14.35-36.patch b/patch/kernel/cubox-default/patch-3.14.35-36.patch index ea99fa6f0..261ccc851 100644 --- a/patch/kernel/cubox-default/patch-3.14.35-36.patch +++ b/patch/kernel/cubox-default/patch-3.14.35-36.patch @@ -1151,35 +1151,6 @@ index a3399c4f13a9..b9b651ea9851 100644 /* Bring BB/MAC out of reset */ iowrite32(regval & ~val, reg); -diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h -index 61f6b21fb0ae..dc6bd8cd9b83 100644 ---- a/drivers/net/wireless/ath/ath6kl/hif.h -+++ b/drivers/net/wireless/ath/ath6kl/hif.h -@@ -197,9 +197,9 @@ struct hif_scatter_req { - /* bounce buffer for upper layers to copy to/from */ - u8 *virt_dma_buf; - -- struct hif_scatter_item scat_list[1]; -- - u32 scat_q_depth; -+ -+ struct hif_scatter_item scat_list[0]; - }; - - struct ath6kl_irq_proc_registers { -diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c -index 7126bdd4236c..6bf15a331714 100644 ---- a/drivers/net/wireless/ath/ath6kl/sdio.c -+++ b/drivers/net/wireless/ath/ath6kl/sdio.c -@@ -348,7 +348,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, - int i, scat_req_sz, scat_list_sz, size; - u8 *virt_buf; - -- scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item); -+ scat_list_sz = n_scat_entry * sizeof(struct hif_scatter_item); - scat_req_sz = sizeof(*s_req) + scat_list_sz; - - if (!virt_scat) diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 5642a9b250c2..953bd0bfdf0d 100644 --- a/drivers/scsi/be2iscsi/be_main.c diff --git a/patch/kernel/cubox-default/patch-3.14.37-38.patch b/patch/kernel/cubox-default/patch-3.14.37-38.patch index a7c872544..561244b46 100644 --- a/patch/kernel/cubox-default/patch-3.14.37-38.patch +++ b/patch/kernel/cubox-default/patch-3.14.37-38.patch @@ -825,18 +825,21 @@ diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 715589ff0eda..e93c36fd3073 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c -@@ -1198,13 +1198,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) - /* Enable VDDC charge pump */ +@@ -1198,16 +1198,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec) + /* Enable VDDC charge pump */ ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP; } else if (vddio >= 3100 && vdda >= 3100) { - /* - * if vddio and vddd > 3.1v, - * charge pump should be clean before set ana_pwr - */ -- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, -- SGTL5000_VDDC_CHRGPMP_POWERUP, 0); +-// FIXME: this is total crap - we have read this register above into +-// ana_pwr, which we then modify (above), and then write back to the +-// register below. This modification just gets completely overwritten. +-// snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, +-// SGTL5000_VDDC_CHRGPMP_POWERUP, 0); - -+ ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP; ++ ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP; /* VDDC use VDDIO rail */ lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD; lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO << diff --git a/patch/kernel/cubox-default/patch-3.14.39-40.patch b/patch/kernel/cubox-default/patch-3.14.39-40.patch index 550549b51..df3ad67dd 100644 --- a/patch/kernel/cubox-default/patch-3.14.39-40.patch +++ b/patch/kernel/cubox-default/patch-3.14.39-40.patch @@ -1122,26 +1122,6 @@ index 80bfa0391913..075e7e7abea9 100644 return num_wrbs; } -diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c -index ad5a5aadc7e1..70eb4d27b4fa 100644 ---- a/drivers/net/ethernet/freescale/gianfar.c -+++ b/drivers/net/ethernet/freescale/gianfar.c -@@ -2152,13 +2152,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) - skb_new = skb_realloc_headroom(skb, fcb_len); - if (!skb_new) { - dev->stats.tx_errors++; -- kfree_skb(skb); -+ dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - - if (skb->sk) - skb_set_owner_w(skb_new, skb->sk); -- consume_skb(skb); -+ dev_consume_skb_any(skb); - skb = skb_new; - } - diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index 57e390cbe6d0..f42c201f727f 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c diff --git a/patch/kernel/odroid-default/fbtft_drivers.patch b/patch/kernel/odroid-default/fbtft_drivers.patch new file mode 100644 index 000000000..8ecb55230 --- /dev/null +++ b/patch/kernel/odroid-default/fbtft_drivers.patch @@ -0,0 +1,12012 @@ +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 03bed11..48f1f71 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/host1x/Kconfig" +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 07905d0..14810e4 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-$(CONFIG_HDMI) += hdmi.o + obj-y += fb_notify.o +diff --git a/drivers/video/fbtft/Kconfig b/drivers/video/fbtft/Kconfig +new file mode 100644 +index 0000000..995a910 +--- /dev/null ++++ b/drivers/video/fbtft/Kconfig +@@ -0,0 +1,169 @@ ++menuconfig FB_TFT ++ tristate "Support for small TFT LCD display modules" ++ depends on FB && SPI && GPIOLIB ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ select FB_SYS_FOPS ++ select FB_DEFERRED_IO ++ select FB_BACKLIGHT ++ ++config FB_TFT_AGM1264K_FL ++ tristate "FB driver for the AGM1264K-FL LCD display" ++ depends on FB_TFT ++ help ++ Framebuffer support for the AGM1264K-FL LCD display (two Samsung KS0108 compatable chips) ++ ++config FB_TFT_BD663474 ++ tristate "FB driver for the BD663474 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for BD663474 ++ ++config FB_TFT_HX8340BN ++ tristate "FB driver for the HX8340BN LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8340BN ++ ++config FB_TFT_HX8347D ++ tristate "FB driver for the HX8347D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8347D ++ ++config FB_TFT_HX8353D ++ tristate "FB driver for the HX8353D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8353D ++ ++config FB_TFT_ILI9320 ++ tristate "FB driver for the ILI9320 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9320 ++ ++config FB_TFT_ILI9325 ++ tristate "FB driver for the ILI9325 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9325 ++ ++config FB_TFT_ILI9340 ++ tristate "FB driver for the ILI9340 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9340 ++ ++config FB_TFT_ILI9341 ++ tristate "FB driver for the ILI9341 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9341 ++ ++config FB_TFT_ILI9481 ++ tristate "FB driver for the ILI9481 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9481 ++ ++config FB_TFT_ILI9486 ++ tristate "FB driver for the ILI9486 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9486 ++ ++config FB_TFT_PCD8544 ++ tristate "FB driver for the PCD8544 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for PCD8544 ++ ++config FB_TFT_RA8875 ++ tristate "FB driver for the RA8875 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for RA8875 ++ ++config FB_TFT_S6D02A1 ++ tristate "FB driver for the S6D02A1 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D02A1 ++ ++config FB_TFT_S6D1121 ++ tristate "FB driver for the S6D1211 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D1121 ++ ++config FB_TFT_SSD1289 ++ tristate "FB driver for the SSD1289 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1289 ++ ++config FB_TFT_SSD1306 ++ tristate "FB driver for the SSD1306 OLED Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1306 ++ ++config FB_TFT_SSD1331 ++ tristate "FB driver for the SSD1331 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1331 ++ ++config FB_TFT_SSD1351 ++ tristate "FB driver for the SSD1351 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1351 ++ ++config FB_TFT_ST7735R ++ tristate "FB driver for the ST7735R LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ST7735R ++ ++config FB_TFT_TINYLCD ++ tristate "FB driver for tinylcd.com display" ++ depends on FB_TFT ++ help ++ Custom Framebuffer support for tinylcd.com display ++ ++config FB_TFT_TLS8204 ++ tristate "FB driver for the TLS8204 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TLS8204 ++ ++config FB_TFT_UC1701 ++ tristate "FB driver for the UC1701 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for UC1701 ++ ++config FB_TFT_UPD161704 ++ tristate "FB driver for the uPD161704 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for uPD161704 ++ ++config FB_TFT_WATTEROTT ++ tristate "FB driver for the WATTEROTT LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for WATTEROTT ++ ++config FB_FLEX ++ tristate "Generic FB driver for TFT LCD displays" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TFT LCD displays. ++ ++config FB_TFT_FBTFT_DEVICE ++ tristate "Module to for adding FBTFT devices" ++ depends on FB_TFT +diff --git a/drivers/video/fbtft/Makefile b/drivers/video/fbtft/Makefile +new file mode 100644 +index 0000000..71c755d +--- /dev/null ++++ b/drivers/video/fbtft/Makefile +@@ -0,0 +1,60 @@ ++ifneq ($(KERNELRELEASE),) ++# kbuild part of makefile ++ ++# Optionally, include config file to allow out of tree kernel modules build ++-include $(src)/.config ++ ++# Core module ++obj-$(CONFIG_FB_TFT) += fbtft.o ++fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o ++ ++# drivers ++obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o ++obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o ++obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o ++obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o ++obj-$(CONFIG_FB_TFT_HX8353D) += fb_hx8353d.o ++obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o ++obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o ++obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o ++obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o ++obj-$(CONFIG_FB_TFT_ILI9481) += fb_ili9481.o ++obj-$(CONFIG_FB_TFT_ILI9486) += fb_ili9486.o ++obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o ++obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o ++obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o ++obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o ++obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o ++obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o ++obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o ++obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o ++obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o ++obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o ++obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o ++obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o ++obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o ++obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o ++obj-$(CONFIG_FB_FLEX) += flexfb.o ++ ++# Device modules ++obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o ++ ++else ++# normal makefile ++KDIR ?= /lib/modules/`uname -r`/build ++ ++default: .config ++ $(MAKE) -C $(KDIR) M=$$PWD modules ++ ++.config: ++ grep config Kconfig | cut -d' ' -f2 | sed 's@^@CONFIG_@; s@$$@=m@' > .config ++ ++install: ++ $(MAKE) -C $(KDIR) M=$$PWD modules_install ++ ++ ++clean: ++ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions \ ++ modules.order Module.symvers ++ ++endif +diff --git a/drivers/video/fbtft/README b/drivers/video/fbtft/README +new file mode 100644 +index 0000000..9bebc98 +--- /dev/null ++++ b/drivers/video/fbtft/README +@@ -0,0 +1,37 @@ ++ FBTFT ++========= ++ ++2015-01-19 ++The FBTFT drivers are now in the Linux kernel staging tree: https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing ++Development in this github repo has ceased. ++ ++ ++Linux Framebuffer drivers for small TFT LCD display modules. ++The module 'fbtft' makes writing drivers for some of these displays very easy. ++ ++Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. ++ ++INSTALLATION ++ Download kernel sources ++ ++ From Linux 3.15 ++ cd drivers/video/fbdev ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" ++ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ ++ ++ Before Linux 3.15 ++ cd drivers/video ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" ++ Add to drivers/video/Makefile: obj-y += fbtft/ ++ ++ Enable driver(s) in menuconfig and build the kernel ++ ++ ++See wiki for more information: https://github.com/notro/fbtft/wiki ++ ++ ++Source: https://github.com/notro/fbtft/ +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +new file mode 100644 +index 0000000..621497f +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +@@ -0,0 +1,87 @@ ++/* ++ * Device Tree overlay for HY28A display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28a_pins: hy28a_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28a: hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28a_pins>; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts: hy28a-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28a>,"spi-max-frequency:0"; ++ rotate = <&hy28a>,"rotate:0"; ++ fps = <&hy28a>,"fps:0"; ++ debug = <&hy28a>,"debug:0"; ++ xohms = <&hy28a_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28a>,"reset-gpios:4", ++ <&hy28a_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28a>,"led-gpios:4", ++ <&hy28a_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +new file mode 100644 +index 0000000..f774c4a +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +@@ -0,0 +1,142 @@ ++/* ++ * Device Tree overlay for HY28b display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28b_pins: hy28b_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28b: hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28b_pins>; ++ ++ spi-max-frequency = <48000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts: hy28b-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28b>,"spi-max-frequency:0"; ++ rotate = <&hy28b>,"rotate:0"; ++ fps = <&hy28b>,"fps:0"; ++ debug = <&hy28b>,"debug:0"; ++ xohms = <&hy28b_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28b>,"reset-gpios:4", ++ <&hy28b_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28b>,"led-gpios:4", ++ <&hy28b_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +new file mode 100644 +index 0000000..c06fe12 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +@@ -0,0 +1,109 @@ ++/* ++ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mz61581_pins: mz61581_pins { ++ brcm,pins = <4 15 18 25>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ mz61581: mz61581@0{ ++ compatible = "samsung,s6d02a1"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mz61581_pins>; ++ ++ spi-max-frequency = <128000000>; ++ spi-cpol; ++ spi-cpha; ++ ++ width = <320>; ++ height = <480>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ ++ reset-gpios = <&gpio 15 0>; ++ dc-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 0>; ++ ++ init = <0x10000b0 00 ++ 0x1000011 ++ 0x20000ff ++ 0x10000b3 0x02 0x00 0x00 0x00 ++ 0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43 ++ 0x10000c1 0x08 0x16 0x08 0x08 ++ 0x10000c4 0x11 0x07 0x03 0x03 ++ 0x10000c6 0x00 ++ 0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00 ++ 0x1000035 0x00 ++ 0x1000036 0xa0 ++ 0x100003a 0x55 ++ 0x1000044 0x00 0x01 ++ 0x10000d0 0x07 0x07 0x1d 0x03 ++ 0x10000d1 0x03 0x30 0x10 ++ 0x10000d2 0x03 0x14 0x04 ++ 0x1000029 ++ 0x100002c>; ++ ++ /* This is a workaround to make sure the init sequence slows down and doesn't fail */ ++ debug = <3>; ++ }; ++ ++ mz61581_ts: mz61581_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <4 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 4 0>; ++ ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&mz61581>, "spi-max-frequency:0"; ++ rotate = <&mz61581>, "rotate:0"; ++ fps = <&mz61581>, "fps:0"; ++ debug = <&mz61581>, "debug:0"; ++ xohms = <&mz61581_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +new file mode 100644 +index 0000000..8cd6a95 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +@@ -0,0 +1,94 @@ ++/* ++ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ piscreen_pins: piscreen_pins { ++ brcm,pins = <17 25 24 22>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ piscreen: piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&piscreen_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ regwidth = <16>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ debug = <0>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000ff ++ 0x100003a 0x55 ++ 0x1000036 0x28 ++ 0x10000c2 0x44 ++ 0x10000c5 0x00 0x00 0x00 0x00 ++ 0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00 ++ 0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0x1000029>; ++ }; ++ ++ piscreen-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&piscreen>,"spi-max-frequency:0"; ++ rotate = <&piscreen>,"rotate:0"; ++ fps = <&piscreen>,"fps:0"; ++ debug = <&piscreen>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +new file mode 100644 +index 0000000..e8a9365 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +@@ -0,0 +1,115 @@ ++/* ++ * Device Tree overlay for pitft resistive by Adafruit ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pitft_pins: pitft_pins { ++ brcm,pins = <24 25>; ++ brcm,function = <0 1>; /* in out */ ++ brcm,pull = <2 0>; /* pullup none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pitft: pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pitft_pins>; ++ ++ spi-max-frequency = <16000000>; ++ rotate = <90>; ++ fps = <25>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ debug = <0>; ++ }; ++ ++ pitft_ts@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "st,stmpe610"; ++ reg = <1>; ++ ++ spi-max-frequency = <500000>; ++ irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */ ++ interrupts = <24 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ ++ stmpe_gpio: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ /* ++ * only GPIO2 is wired/available ++ * and it is wired to the backlight ++ */ ++ st,norequest-mask = <0x7b>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/soc"; ++ __overlay__ { ++ backlight { ++ compatible = "gpio-backlight"; ++ gpios = <&stmpe_gpio 2 0>; ++ default-on; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&pitft>,"spi-max-frequency:0"; ++ rotate = <&pitft>,"rotate:0"; ++ fps = <&pitft>,"fps:0"; ++ debug = <&pitft>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +new file mode 100644 +index 0000000..0578810 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +@@ -0,0 +1,81 @@ ++/* ++ * Device Tree overlay for rpi-display by Watterott ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_display_pins: rpi_display_pins { ++ brcm,pins = <18 23 24 25>; ++ brcm,function = <1 1 1 0>; /* out out out in */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rpidisplay: rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_display_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpidisplay_ts: rpi-display-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <25 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&rpidisplay>,"spi-max-frequency:0"; ++ rotate = <&rpidisplay>,"rotate:0"; ++ fps = <&rpidisplay>,"fps:0"; ++ debug = <&rpidisplay>,"debug:0"; ++ xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +new file mode 100644 +index 0000000..881d2eb +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +@@ -0,0 +1,181 @@ ++/* ++ * tinylcd 3.5" display ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ tinylcd35_pins: tinylcd35_pins { ++ brcm,pins = <25 24 18>; ++ brcm,function = <1>; /* out */ ++ }; ++ tinylcd35_ts_pins: tinylcd35_ts_pins { ++ brcm,pins = <5>; ++ brcm,function = <0>; /* in */ ++ }; ++ keypad_pins: keypad_pins { ++ brcm,pins = <4 17 22 23 27>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <1>; /* down */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ tinylcd35: tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tinylcd35_pins>, ++ <&tinylcd35_ts_pins>; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ fps = <20>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ ++ init = <0x10000B0 0x80 ++ 0x10000C0 0x0A 0x0A ++ 0x10000C1 0x01 0x01 ++ 0x10000C2 0x33 ++ 0x10000C5 0x00 0x42 0x80 ++ 0x10000B1 0xD0 0x11 ++ 0x10000B4 0x02 ++ 0x10000B6 0x00 0x22 0x3B ++ 0x10000B7 0x07 ++ 0x1000036 0x58 ++ 0x10000F0 0x36 0xA5 0xD3 ++ 0x10000E5 0x80 ++ 0x10000E5 0x01 ++ 0x10000B3 0x00 ++ 0x10000E5 0x00 ++ 0x10000F0 0x36 0xA5 0x53 ++ 0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00 ++ 0x100003A 0x55 ++ 0x1000011 ++ 0x2000001 ++ 0x1000029>; ++ }; ++ ++ tinylcd35_ts: tinylcd35_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <5 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 5 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pcf8563: pcf8563@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* ++ * Values for input event code is found under the ++ * 'Keys and buttons' heading in include/uapi/linux/input.h ++ */ ++ fragment@4 { ++ target-path = "/soc"; ++ __overlay__ { ++ keypad: keypad { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&keypad_pins>; ++ status = "disabled"; ++ autorepeat; ++ ++ button@17 { ++ label = "GPIO KEY_UP"; ++ linux,code = <103>; ++ gpios = <&gpio 17 0>; ++ }; ++ button@22 { ++ label = "GPIO KEY_DOWN"; ++ linux,code = <108>; ++ gpios = <&gpio 22 0>; ++ }; ++ button@27 { ++ label = "GPIO KEY_LEFT"; ++ linux,code = <105>; ++ gpios = <&gpio 27 0>; ++ }; ++ button@23 { ++ label = "GPIO KEY_RIGHT"; ++ linux,code = <106>; ++ gpios = <&gpio 23 0>; ++ }; ++ button@4 { ++ label = "GPIO KEY_ENTER"; ++ linux,code = <28>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&tinylcd35>,"spi-max-frequency:0"; ++ rotate = <&tinylcd35>,"rotate:0"; ++ fps = <&tinylcd35>,"fps:0"; ++ debug = <&tinylcd35>,"debug:0"; ++ touch = <&tinylcd35_ts>,"status"; ++ touchgpio = <&tinylcd35_ts_pins>,"brcm,pins:0", ++ <&tinylcd35_ts>,"interrupts:0", ++ <&tinylcd35_ts>,"pendown-gpio:4"; ++ xohms = <&tinylcd35_ts>,"ti,x-plate-ohms;0"; ++ rtc = <&i2c1>,"status", ++ <&pcf8563>,"status"; ++ keypad = <&keypad>,"status"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/rpi.dts b/drivers/video/fbtft/dts/rpi.dts +new file mode 100644 +index 0000000..6c3ab6e +--- /dev/null ++++ b/drivers/video/fbtft/dts/rpi.dts +@@ -0,0 +1,414 @@ ++ ++/* ++ * FBTFT Device Tree part for the Raspberry Pi ++ * Add this file to the end of the *rpi-b.dts file ++ * ++ * Please keep it sorted alphabetically on display name ++ * ++ * Notes: ++ * - use ads7846 instead of tsc2046 to get module autoloading ++ * - use polarity 1 to drive backlight initially low (off): ++ * led-gpios = <&gpio 18 1>; ++ */ ++ ++&spi0 { ++ /* this is provided here to make it easy to enable SPI when editing this file */ ++// status = "okay"; ++}; ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28A ++ * ++ */ ++&spi0 { ++ hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28B ++ * NOT TESTED ++ */ ++&spi0 { ++ hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * ITEAD ++ * ITDB02-2.8 ++ * ++ */ ++/ { ++ itdb28 { ++ compatible = "ilitek,ili9325"; ++ status = "disabled"; ++ ++ rotate = <0>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 17 0>; ++ dc-gpios = <&gpio 3 0>; ++ cs-gpios = <&gpio 27 0>; ++ wr-gpios = <&gpio 2 0>; ++ db-gpios = <&gpio 9 0>, ++ <&gpio 11 0>, ++ <&gpio 18 0>, ++ <&gpio 23 0>, ++ <&gpio 24 0>, ++ <&gpio 25 0>, ++ <&gpio 8 0>, ++ <&gpio 7 0>; ++ /* LED pin drives backlight directly. Use transistor (50mA) */ ++ /* led-gpios = <&gpio 4 1>; */ ++ debug = <0>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Watterott ++ * RPi-Display - 2.8" Touch-Display (320x240) ++ * ++ */ ++&spi0 { ++ rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpi-display_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 25>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Ozzmaker ++ * PiScreen - 3.5" TFT touchscreen (480x320) ++ * ++ */ ++&spi0 { ++ piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <20000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000FF ++ 0x100003A 0x55 ++ 0x1000036 0x28 ++ 0x10000C2 0x44 ++ 0x10000C5 0x00 0x00 0x00 0x00 ++ 0x10000E0 0x0F 0x1F 0x1C 0x0C 0x0F 0x08 0x48 0x98 0x37 0x0A 0x13 0x04 0x11 0x0D 0x00 ++ 0x10000E1 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000E2 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0X1000029>; ++ debug = <0>; ++ }; ++ ++ piscreen_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Adafruit ++ * PiTFT Mini Kit - 320x240 2.8" TFT+Touchscreen for Raspberry Pi ++ * ++ * Couldn't get touch controller to work ++ */ ++ ++&gpio { ++ stmpets_pins: stmpets_pins { ++ brcm,pins = <24>; ++ brcm,function = <0>; /* gpio in */ ++ brcm,pull = <2>; /* pull up */ ++ }; ++}; ++ ++&spi0 { ++ pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <90>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ ++ init = <0x1000001 ++ 0x2000005 ++ 0x1000028 ++ 0x10000EF 0x03 0x80 0x02 ++ 0x10000CF 0x00 0xC1 0x30 ++ 0x10000ED 0x64 0x03 0x12 0x81 ++ 0x10000E8 0x85 0x00 0x78 ++ 0x10000CB 0x39 0x2C 0x00 0x34 0x02 ++ 0x10000F7 0x20 ++ 0x10000EA 0x00 0x00 ++ 0x10000C0 0x23 ++ 0x10000C1 0x10 ++ 0x10000C5 0x3e 0x28 ++ 0x10000C7 0x86 ++ 0x100003A 0x55 ++ 0x10000B1 0x00 0x18 ++ 0x10000B6 0x08 0x82 0x27 ++ 0x10000F2 0x00 ++ 0x1000026 0x01 ++ 0x10000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00 ++ 0x10000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F ++ 0x1000011 ++ 0x2000064 ++ 0x1000029 ++ 0x2000014>; ++ debug = <0>; ++ }; ++/* ++Touchscreen didn't work. I guess it has something to do with it being an interrupt controller. ++I have never tried this before with DT and ARCH_BCM2708. ++On ARCH_BCM2835 it should be OK, since the GPIO controller acts as an interrupt controller as well. ++(stmpe_device can't be used from 3.16, because irq_base can't be set anymore) ++ ++[ 8.889056] irq: irq_create_mapping(0xc3008800, 0xc2) ++[ 8.895698] irq: -> using domain @c3008800 ++[ 8.900796] irq: -> existing mapping on virq 194 ++[ 8.925048] stmpe-spi spi0.1: stmpe610 detected, chip id: 0x811 ++[ 8.936650] irq: Added domain (null) ++[ 8.941780] irq: irq_create_mapping(0xc1dc1a20, 0x0) ++[ 8.949047] irq: -> using domain @c1dc1a20 ++[ 8.954421] irq: -> virq allocation failed ++[ 8.959926] irq: irq_create_mapping(0xc1dc1a20, 0x1) ++[ 8.967366] irq: -> using domain @c1dc1a20 ++[ 8.972870] irq: -> virq allocation failed ++*/ ++ pitft_ts@1 { ++ compatible = "st,stmpe610"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ status = "disabled"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&stmpets_pins>; ++ ++ spi-max-frequency = <500000>; ++ interrupts = <3 24>; ++ interrupt-parent = <&intc>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * NeoSec LLC ++ * 3.5 inch TFT Display (320x480) ++ * ++ */ ++&spi0 { ++ tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ tinylcd35@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 3>; ++ pendown-gpio = <&gpio 3 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; +diff --git a/drivers/video/fbtft/fb_agm1264k-fl.c b/drivers/video/fbtft/fb_agm1264k-fl.c +new file mode 100644 +index 0000000..7fe4fa0 +--- /dev/null ++++ b/drivers/video/fbtft/fb_agm1264k-fl.c +@@ -0,0 +1,462 @@ ++/* ++ * FB driver for Two KS0108 LCD controllers in AGM1264K-FL display ++ * ++ * Copyright (C) 2014 ololoshka2871 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++/* Uncomment text line to use negative image on display */ ++/*#define NEGATIVE*/ ++ ++#define WHITE 0xff ++#define BLACK 0 ++ ++#define DRVNAME "fb_agm1264k-fl" ++#define WIDTH 64 ++#define HEIGHT 64 ++#define TOTALWIDTH (WIDTH * 2) /* because 2 x ks0108 in one display */ ++#define FPS 20 ++ ++#define EPIN gpio.wr ++#define RS gpio.dc ++#define RW gpio.aux[2] ++#define CS0 gpio.aux[0] ++#define CS1 gpio.aux[1] ++ ++ ++/* diffusing error (“Floyd-Steinberg”) */ ++#define DIFFUSING_MATRIX_WIDTH 2 ++#define DIFFUSING_MATRIX_HEIGHT 2 ++ ++static const signed char ++diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = { ++ {-1, 3}, ++ {3, 2}, ++}; ++ ++static const unsigned char gamma_correction_table[] = { ++0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, ++1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, ++6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, ++13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, ++22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, ++33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, ++46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ++62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81, ++82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102, ++103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121, ++123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143, ++145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166, ++168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192, ++194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, ++221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248, ++251, 253, 255 ++}; ++ ++static int init_display(struct fbtft_par *par) ++{ ++ u8 i; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ for (i = 0; i < 2; ++i) { ++ write_reg(par, i, 0x3f); /* display on */ ++ write_reg(par, i, 0x40); /* set x to 0 */ ++ write_reg(par, i, 0xb0); /* set page to 0 */ ++ write_reg(par, i, 0xc0); /* set start line to 0 */ ++ } ++ ++ return 0; ++} ++ ++void reset(struct fbtft_par *par) ++{ ++ if (par->gpio.reset == -1) ++ return; ++ ++ fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__); ++ ++ gpio_set_value(par->gpio.reset, 0); ++ udelay(20); ++ gpio_set_value(par->gpio.reset, 1); ++ mdelay(120); ++} ++ ++/* Check if all necessary GPIOS defined */ ++static int verify_gpios(struct fbtft_par *par) ++{ ++ int i; ++ ++ fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device, ++ "%s()\n", __func__); ++ ++ if (par->EPIN < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'wr' (aka E) gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ for (i = 0; i < 8; ++i) { ++ if (par->gpio.db[i] < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'db[%i]' gpio. Aborting.\n", ++ i); ++ return -EINVAL; ++ } ++ } ++ if (par->CS0 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs0' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->CS1 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs1' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->RW < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'rw' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static unsigned long ++request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio) ++{ ++ fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device, ++ "%s('%s')\n", __func__, gpio->name); ++ ++ if (strcasecmp(gpio->name, "wr") == 0) { ++ /* left ks0108 E pin */ ++ par->EPIN = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } else if (strcasecmp(gpio->name, "cs0") == 0) { ++ /* left ks0108 controller pin */ ++ par->CS0 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } else if (strcasecmp(gpio->name, "cs1") == 0) { ++ /* right ks0108 controller pin */ ++ par->CS1 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } ++ ++ /* if write (rw = 0) e(1->0) perform write */ ++ /* if read (rw = 1) e(0->1) set data on D0-7*/ ++ else if (strcasecmp(gpio->name, "rw") == 0) { ++ par->RW = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } ++ ++ return FBTFT_GPIO_NO_MATCH; ++} ++ ++/* This function oses to enter commands ++ * first byte - destination controller 0 or 1 ++ * folowing - commands ++ */ ++static void write_reg8_bus8(struct fbtft_par *par, int len, ...) ++{ ++ va_list args; ++ int i, ret; ++ u8 *buf = (u8 *)par->buf; ++ ++ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { ++ va_start(args, len); ++ for (i = 0; i < len; i++) ++ buf[i] = (u8)va_arg(args, unsigned int); ++ ++ va_end(args); ++ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, ++ par->info->device, u8, buf, len, "%s: ", __func__); ++ } ++ ++ va_start(args, len); ++ ++ *buf = (u8)va_arg(args, unsigned int); ++ ++ if (*buf > 1) { ++ va_end(args); ++ dev_err(par->info->device, "%s: Incorrect chip sellect request (%d)\n", ++ __func__, *buf); ++ return; ++ } ++ ++ /* select chip */ ++ if (*buf) { ++ /* cs1 */ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 0); ++ } else { ++ /* cs0 */ ++ gpio_set_value(par->CS0, 0); ++ gpio_set_value(par->CS1, 1); ++ } ++ ++ gpio_set_value(par->RS, 0); /* RS->0 (command mode) */ ++ len--; ++ ++ if (len) { ++ i = len; ++ while (i--) ++ *buf++ = (u8)va_arg(args, unsigned int); ++ ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8))); ++ if (ret < 0) { ++ va_end(args); ++ dev_err(par->info->device, "%s: write() failed and returned %d\n", ++ __func__, ret); ++ return; ++ } ++ } ++ ++ va_end(args); ++} ++ ++static struct ++{ ++ int xs, ys_page, xe, ye_page; ++} addr_win; ++ ++/* save display writing zone */ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ addr_win.xs = xs; ++ addr_win.ys_page = ys / 8; ++ addr_win.xe = xe; ++ addr_win.ye_page = ye / 8; ++ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__, ++ addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page); ++} ++ ++static void ++construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src, ++ int xs, int xe, int y) ++{ ++ int x, i; ++ ++ for (x = xs; x < xe; ++x) { ++ u8 res = 0; ++ ++ for (i = 0; i < 8; i++) ++ if (src[(y * 8 + i) * par->info->var.xres + x]) ++ res |= 1 << i; ++#ifdef NEGATIVE ++ *dest++ = res; ++#else ++ *dest++ = ~res; ++#endif ++ } ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y; ++ int ret = 0; ++ ++ /* buffer to convert RGB565 -> grayscale16 -> Ditherd image 1bpp */ ++ signed short *convert_buf = kmalloc(par->info->var.xres * ++ par->info->var.yres * sizeof(signed short), GFP_NOIO); ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ /* converting to grayscale16 */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ u16 pixel = vmem16[y * par->info->var.xres + x]; ++ u16 b = pixel & 0x1f; ++ u16 g = (pixel & (0x3f << 5)) >> 5; ++ u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6); ++ ++ pixel = (299 * r + 587 * g + 114 * b) / 200; ++ if (pixel > 255) ++ pixel = 255; ++ ++ /* gamma-correction by table */ ++ convert_buf[y * par->info->var.xres + x] = ++ (signed short)gamma_correction_table[pixel]; ++ } ++ ++ /* Image Dithering */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ signed short pixel = ++ convert_buf[y * par->info->var.xres + x]; ++ signed short error_b = pixel - BLACK; ++ signed short error_w = pixel - WHITE; ++ signed short error; ++ u16 i, j; ++ ++ /* what color close? */ ++ if (abs(error_b) >= abs(error_w)) { ++ /* white */ ++ error = error_w; ++ pixel = 0xff; ++ } else { ++ /* black */ ++ error = error_b; ++ pixel = 0; ++ } ++ ++ error /= 8; ++ ++ /* diffusion matrix row */ ++ for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i) ++ /* diffusion matrix column */ ++ for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) { ++ signed short *write_pos; ++ signed char coeff; ++ ++ /* skip pixels out of zone */ ++ if (x + i < 0 || ++ x + i >= par->info->var.xres ++ || y + j >= par->info->var.yres) ++ continue; ++ write_pos = &convert_buf[ ++ (y + j) * par->info->var.xres + ++ x + i]; ++ coeff = diffusing_matrix[i][j]; ++ if (coeff == -1) ++ /* pixel itself */ ++ *write_pos = pixel; ++ else { ++ signed short p = *write_pos + ++ error * coeff; ++ ++ if (p > WHITE) ++ p = WHITE; ++ if (p < BLACK) ++ p = BLACK; ++ *write_pos = p; ++ } ++ } ++ } ++ ++ /* 1 string = 2 pages */ ++ for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) { ++ /* left half of display */ ++ if (addr_win.xs < par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, convert_buf, ++ addr_win.xs, par->info->var.xres / 2, y); ++ ++ len = par->info->var.xres / 2 - addr_win.xs; ++ ++ /* select left side (sc0) ++ * set addr ++ */ ++ write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs); ++ write_reg(par, 0x00, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ ret = par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ /* right half of display */ ++ if (addr_win.xe >= par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, ++ convert_buf, par->info->var.xres / 2, ++ addr_win.xe + 1, y); ++ ++ len = addr_win.xe + 1 - par->info->var.xres / 2; ++ ++ /* select right side (sc1) ++ * set addr ++ */ ++ write_reg(par, 0x01, (1 << 6)); ++ write_reg(par, 0x01, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ } ++ kfree(convert_buf); ++ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 1); ++ ++ return ret; ++} ++ ++static int write(struct fbtft_par *par, void *buf, size_t len) ++{ ++ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, ++ "%s(len=%d): ", __func__, len); ++ ++ gpio_set_value(par->RW, 0); /* set write mode */ ++ ++ ++ while (len--) { ++ u8 i, data; ++ ++ data = *(u8 *) buf++; ++ ++ /* set data bus */ ++ for (i = 0; i < 8; ++i) ++ gpio_set_value(par->gpio.db[i], data & (1 << i)); ++ /* set E */ ++ gpio_set_value(par->EPIN, 1); ++ udelay(5); ++ /* unset E - write */ ++ gpio_set_value(par->EPIN, 0); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = TOTALWIDTH, ++ .height = HEIGHT, ++ .fps = FPS, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .verify_gpios = verify_gpios, ++ .request_gpios_match = request_gpios_match, ++ .reset = reset, ++ .write = write, ++ .write_register = write_reg8_bus8, ++ .write_vmem = write_vmem, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display); ++ ++MODULE_ALIAS("platform:" DRVNAME); ++ ++MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display"); ++MODULE_AUTHOR("ololoshka2871"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_bd663474.c b/drivers/video/fbtft/fb_bd663474.c +new file mode 100644 +index 0000000..7e00c60 +--- /dev/null ++++ b/drivers/video/fbtft/fb_bd663474.c +@@ -0,0 +1,193 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "hitachi,bd663474", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:bd663474"); ++MODULE_ALIAS("platform:bd663474"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8340bn.c b/drivers/video/fbtft/fb_hx8340bn.c +new file mode 100644 +index 0000000..3939502 +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8340bn.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the HX8340BN LCD Controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8340bn", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8340bn"); ++MODULE_ALIAS("platform:hx8340bn"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8347d.c b/drivers/video/fbtft/fb_hx8347d.c +new file mode 100644 +index 0000000..8139a8f +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8347d.c +@@ -0,0 +1,181 @@ ++/* ++ * FB driver for the HX8347D LCD Controller ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * ++ * Based on driver code found here: https://github.com/watterott/r61505u-Adapter ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8347d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8347d"); ++MODULE_ALIAS("platform:hx8347d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8353d.c b/drivers/video/fbtft/fb_hx8353d.c +new file mode 100644 +index 0000000..c9512dc +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8353d.c +@@ -0,0 +1,166 @@ ++/* ++ * FB driver for the HX8353D LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8353d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8353d"); ++MODULE_ALIAS("platform:hx8353d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9320.c b/drivers/video/fbtft/fb_ili9320.c +new file mode 100644 +index 0000000..b26d893 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9320.c +@@ -0,0 +1,234 @@ ++/* ++ * FB driver for the ILI9320 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9320", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9320"); ++MODULE_ALIAS("platform:ili9320"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9325.c b/drivers/video/fbtft/fb_ili9325.c +new file mode 100644 +index 0000000..5f88145 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9325.c +@@ -0,0 +1,291 @@ ++/* ++ * FB driver for the ILI9325 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Based on ili9325.c by Jeroen Domburg ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9325", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9325"); ++MODULE_ALIAS("platform:ili9325"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9340.c b/drivers/video/fbtft/fb_ili9340.c +new file mode 100644 +index 0000000..985687d +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9340.c +@@ -0,0 +1,163 @@ ++/* ++ * FB driver for the ILI9340 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9340", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9340"); ++MODULE_ALIAS("platform:ili9340"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9341.c b/drivers/video/fbtft/fb_ili9341.c +new file mode 100644 +index 0000000..225b2d8 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9341.c +@@ -0,0 +1,179 @@ ++/* ++ * FB driver for the ILI9341 LCD display controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * Based on adafruit22fb.c by Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9341", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9341"); ++MODULE_ALIAS("platform:ili9341"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9481.c b/drivers/video/fbtft/fb_ili9481.c +new file mode 100644 +index 0000000..725157a +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9481.c +@@ -0,0 +1,117 @@ ++/* ++ * FB driver for the ILI9481 LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_ili9481" ++#define WIDTH 320 ++#define HEIGHT 480 ++ ++static int default_init_sequence[] = { ++ ++ /* SLP_OUT - Sleep out */ ++ -1, 0x11, ++ -2, 50, ++ /* Power setting */ ++ -1, 0xD0, 0x07, 0x42, 0x18, ++ /* VCOM */ ++ -1, 0xD1, 0x00, 0x07, 0x10, ++ /* Power setting for norm. mode */ ++ -1, 0xD2, 0x01, 0x02, ++ /* Panel driving setting */ ++ -1, 0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11, ++ /* Frame rate & inv. */ ++ -1, 0xC5, 0x03, ++ /* Pixel format */ ++ -1, 0x3A, 0x55, ++ /* Gamma */ ++ -1, 0xC8, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16, ++ 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00, ++ /* DISP_ON */ ++ -1, 0x29, ++ -3 ++}; ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* column address */ ++ write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff); ++ ++ /* row adress */ ++ write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff); ++ ++ /* memory write */ ++ write_reg(par, 0x2c); ++} ++ ++#define HFLIP 0x01 ++#define VFLIP 0x02 ++#define ROWxCOL 0x20 ++static int set_var(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ switch (par->info->var.rotate) { ++ case 270: ++ write_reg(par, 0x36, ROWxCOL | HFLIP | VFLIP | (par->bgr << 3)); ++ break; ++ case 180: ++ write_reg(par, 0x36, VFLIP | (par->bgr << 3)); ++ break; ++ case 90: ++ write_reg(par, 0x36, ROWxCOL | (par->bgr << 3)); ++ break; ++ default: ++ write_reg(par, 0x36, HFLIP | (par->bgr << 3)); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9481", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9481"); ++MODULE_ALIAS("platform:ili9481"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9481 LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9486.c b/drivers/video/fbtft/fb_ili9486.c +new file mode 100644 +index 0000000..95b8999 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9486.c +@@ -0,0 +1,121 @@ ++/* ++ * FB driver for the ILI9486 LCD Controller ++ * ++ * Copyright (C) 2014 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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 = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9486", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9486"); ++MODULE_ALIAS("platform:ili9486"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_pcd8544.c b/drivers/video/fbtft/fb_pcd8544.c +new file mode 100644 +index 0000000..678ab8e +--- /dev/null ++++ b/drivers/video/fbtft/fb_pcd8544.c +@@ -0,0 +1,177 @@ ++/* ++ * FB driver for the PCD8544 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "philips,pdc8544", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:pdc8544"); ++ ++MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ra8875.c b/drivers/video/fbtft/fb_ra8875.c +new file mode 100644 +index 0000000..c323c06 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ra8875.c +@@ -0,0 +1,331 @@ ++/****************************************************************************** ++ ++ 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, "raio,ra8875", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ra8875"); ++MODULE_ALIAS("platform:ra8875"); ++ ++MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller"); ++MODULE_AUTHOR("Pf@nne"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d02a1.c b/drivers/video/fbtft/fb_s6d02a1.c +new file mode 100644 +index 0000000..e412a42 +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d02a1.c +@@ -0,0 +1,168 @@ ++/* ++ * FB driver for the S6D02A1 LCD Controller ++ * ++ * Based on fb_st7735r.c by Noralf Tronnes ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d02a1", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d02a1"); ++MODULE_ALIAS("platform:s6d02a1"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller"); ++MODULE_AUTHOR("WOLFGANG BUENING"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d1121.c b/drivers/video/fbtft/fb_s6d1121.c +new file mode 100644 +index 0000000..1ef8c1a +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d1121.c +@@ -0,0 +1,208 @@ ++/* ++ * FB driver for the S6D1121 LCD Controller ++ * ++ * Copyright (C) 2013 Roman Rolinsky ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d1121", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d1121"); ++MODULE_ALIAS("platform:s6d1121"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller"); ++MODULE_AUTHOR("Roman Rolinsky"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1289.c b/drivers/video/fbtft/fb_ssd1289.c +new file mode 100644 +index 0000000..ef46fbc +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1289.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the SSD1289 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1289", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1289"); ++MODULE_ALIAS("platform:ssd1289"); ++ ++MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1306.c b/drivers/video/fbtft/fb_ssd1306.c +new file mode 100644 +index 0000000..5ea195b +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1306.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the SSD1306 OLED Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1306", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1306"); ++MODULE_ALIAS("platform:ssd1306"); ++ ++MODULE_DESCRIPTION("SSD1306 OLED Driver"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1331.c b/drivers/video/fbtft/fb_ssd1331.c +new file mode 100644 +index 0000000..da7464f +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1331.c +@@ -0,0 +1,205 @@ ++#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, "solomon,ssd1331", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1331"); ++MODULE_ALIAS("platform:ssd1331"); ++ ++MODULE_DESCRIPTION("SSD1331 OLED Driver"); ++MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1351.c b/drivers/video/fbtft/fb_ssd1351.c +new file mode 100644 +index 0000000..062d986 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1351.c +@@ -0,0 +1,258 @@ ++#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, "solomon,ssd1351", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1351"); ++MODULE_ALIAS("platform:ssd1351"); ++ ++MODULE_DESCRIPTION("SSD1351 OLED Driver"); ++MODULE_AUTHOR("James Davies"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_st7735r.c b/drivers/video/fbtft/fb_st7735r.c +new file mode 100644 +index 0000000..b63aa38 +--- /dev/null ++++ b/drivers/video/fbtft/fb_st7735r.c +@@ -0,0 +1,195 @@ ++/* ++ * FB driver for the ST7735R LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "sitronix,st7735r", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:st7735r"); ++MODULE_ALIAS("platform:st7735r"); ++ ++MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tinylcd.c b/drivers/video/fbtft/fb_tinylcd.c +new file mode 100644 +index 0000000..ca98bfb +--- /dev/null ++++ b/drivers/video/fbtft/fb_tinylcd.c +@@ -0,0 +1,124 @@ ++/* ++ * Custom FB driver for tinylcd.com display ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "neosec,tinylcd", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tinylcd"); ++ ++MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tls8204.c b/drivers/video/fbtft/fb_tls8204.c +new file mode 100644 +index 0000000..8738c7a +--- /dev/null ++++ b/drivers/video/fbtft/fb_tls8204.c +@@ -0,0 +1,176 @@ ++/* ++ * FB driver for the TLS8204 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * Copyright (C) 2014 Michael Hope (adapted for the TLS8204) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "teralane,tls8204", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tls8204"); ++ ++MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller"); ++MODULE_AUTHOR("Michael Hope"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_uc1701.c b/drivers/video/fbtft/fb_uc1701.c +new file mode 100644 +index 0000000..d70ac52 +--- /dev/null ++++ b/drivers/video/fbtft/fb_uc1701.c +@@ -0,0 +1,210 @@ ++/* ++ * FB driver for the UC1701 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2014 Juergen Holzmann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_uc1701" ++#define WIDTH 102 ++#define HEIGHT 64 ++#define PAGES (HEIGHT/8) ++ ++/* 1: Display on/off */ ++#define LCD_DISPLAY_ENABLE 0xAE ++/* 2: display start line set */ ++#define LCD_START_LINE 0x40 ++/* 3: Page address set (lower 4 bits select one of the pages) */ ++#define LCD_PAGE_ADDRESS 0xB0 ++/* 4: column address */ ++#define LCD_COL_ADDRESS 0x10 ++/* 8: select orientation */ ++#define LCD_BOTTOMVIEW 0xA0 ++/* 9: inverted display */ ++#define LCD_DISPLAY_INVERT 0xA6 ++/* 10: show memory content or switch all pixels on */ ++#define LCD_ALL_PIXEL 0xA4 ++/* 11: lcd bias set */ ++#define LCD_BIAS 0xA2 ++/* 14: Reset Controller */ ++#define LCD_RESET_CMD 0xE2 ++/* 15: output mode select (turns display upside-down) */ ++#define LCD_SCAN_DIR 0xC0 ++/* 16: power control set */ ++#define LCD_POWER_CONTROL 0x28 ++/* 17: voltage regulator resistor ratio set */ ++#define LCD_VOLTAGE 0x20 ++/* 18: Volume mode set */ ++#define LCD_VOLUME_MODE 0x81 ++/* 22: NOP command */ ++#define LCD_NO_OP 0xE3 ++/* 25: advanced program control */ ++#define LCD_ADV_PROG_CTRL 0xFA ++/* 25: advanced program control2 */ ++#define LCD_ADV_PROG_CTRL2 0x10 ++#define LCD_TEMPCOMP_HIGH 0x80 ++/* column offset for normal orientation */ ++#define SHIFT_ADDR_NORMAL 0 ++/* column offset for bottom view orientation */ ++#define SHIFT_ADDR_TOPVIEW 30 ++ ++ ++static int init_display(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ /* softreset of LCD */ ++ write_reg(par, LCD_RESET_CMD); ++ mdelay(10); ++ ++ /* set startpoint */ ++ /* LCD_START_LINE | (pos & 0x3F) */ ++ write_reg(par, LCD_START_LINE); ++ ++ /* select orientation BOTTOMVIEW */ ++ write_reg(par, LCD_BOTTOMVIEW | 1); ++ /* output mode select (turns display upside-down) */ ++ write_reg(par, LCD_SCAN_DIR | 0x00); ++ ++ /* Normal Pixel mode */ ++ write_reg(par, LCD_ALL_PIXEL | 0); ++ ++ /* positive display */ ++ write_reg(par, LCD_DISPLAY_INVERT | 0); ++ ++ /* bias 1/9 */ ++ write_reg(par, LCD_BIAS | 0); ++ ++ /* power control mode: all features on */ ++ /* LCD_POWER_CONTROL | (val&0x07) */ ++ write_reg(par, LCD_POWER_CONTROL | 0x07); ++ ++ /* set voltage regulator R/R */ ++ /* LCD_VOLTAGE | (val&0x07) */ ++ write_reg(par, LCD_VOLTAGE | 0x07); ++ ++ /* volume mode set */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_VOLUME_MODE); ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, 0x09); ++ /* ???? */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_NO_OP); ++ ++ /* advanced program control */ ++ write_reg(par, LCD_ADV_PROG_CTRL); ++ write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH); ++ ++ /* enable display */ ++ write_reg(par, LCD_DISPLAY_ENABLE | 1); ++ ++ return 0; ++} ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* goto address */ ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y, i; ++ int ret = 0; ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ for (y = 0; y < PAGES; y++) { ++ buf = par->txbuf.buf; ++ for (x = 0; x < WIDTH; x++) { ++ *buf = 0x00; ++ for (i = 0; i < 8; i++) ++ *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i; ++ buf++; ++ } ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS|(u8)y); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++ gpio_set_value(par->gpio.dc, 1); ++ ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); ++ gpio_set_value(par->gpio.dc, 0); ++ } ++ ++ if (ret < 0) ++ dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret); ++ ++ return ret; ++} ++ ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .write_vmem = write_vmem, ++ }, ++ .backlight = 1, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:uc1701"); ++ ++MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller"); ++MODULE_AUTHOR("Juergen Holzmann"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_upd161704.c b/drivers/video/fbtft/fb_upd161704.c +new file mode 100644 +index 0000000..fff57b3 +--- /dev/null ++++ b/drivers/video/fbtft/fb_upd161704.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "nec,upd161704", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:upd161704"); ++MODULE_ALIAS("platform:upd161704"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_watterott.c b/drivers/video/fbtft/fb_watterott.c +new file mode 100644 +index 0000000..975b579 +--- /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, "watterott,openlcd", &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..873e2c7 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft-core.c +@@ -0,0 +1,1516 @@ ++/* ++ * 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 ++#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 = true; ++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 ret; ++ ++ 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 = devm_gpio_request_one(par->info->device, ++ 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; ++} ++ ++#ifdef CONFIG_OF ++static int fbtft_request_one_gpio(struct fbtft_par *par, ++ const char *name, int index, int *gpiop) ++{ ++ struct device *dev = par->info->device; ++ struct device_node *node = dev->of_node; ++ int gpio, flags, ret = 0; ++ enum of_gpio_flags of_flags; ++ ++ if (of_find_property(node, name, NULL)) { ++ gpio = of_get_named_gpio_flags(node, name, index, &of_flags); ++ if (gpio == -ENOENT) ++ return 0; ++ if (gpio == -EPROBE_DEFER) ++ return gpio; ++ if (gpio < 0) { ++ dev_err(dev, ++ "failed to get '%s' from DT\n", name); ++ return gpio; ++ } ++ ++ /* active low translates to initially low */ ++ flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : ++ GPIOF_OUT_INIT_HIGH; ++ ret = devm_gpio_request_one(dev, gpio, flags, ++ dev->driver->name); ++ if (ret) { ++ dev_err(dev, ++ "gpio_request_one('%s'=%d) failed with %d\n", ++ name, gpio, ret); ++ return ret; ++ } ++ if (gpiop) ++ *gpiop = gpio; ++ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", ++ __func__, name, gpio); ++ } ++ ++ return ret; ++} ++ ++static int fbtft_request_gpios_dt(struct fbtft_par *par) ++{ ++ int i; ++ int ret; ++ ++ if (!par->info->device->of_node) ++ return -EINVAL; ++ ++ ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch); ++ if (ret) ++ return ret; ++ for (i = 0; i < 16; i++) { ++ ret = fbtft_request_one_gpio(par, "db-gpios", i, ++ &par->gpio.db[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "led-gpios", i, ++ &par->gpio.led[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "aux-gpios", i, ++ &par->gpio.aux[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#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->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; ++ int vmem_size, i; ++ 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; ++ ++ if (!pdata) { ++ dev_err(dev, "platform data is missing\n"); ++ return NULL; ++ } ++ ++ /* override driver values? */ ++ if (pdata->fps) ++ fps = pdata->fps; ++ if (pdata->txbuflen) ++ txbuflen = pdata->txbuflen; ++ 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; ++ 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; ++ if (pdata->display.regwidth) ++ display->regwidth = pdata->display.regwidth; ++ ++ display->debug |= debug; ++ fbtft_expand_debug_value(&display->debug); ++ ++ switch (pdata->rotate) { ++ case 90: ++ case 270: ++ width = display->height; ++ height = display->width; ++ break; ++ default: ++ width = display->width; ++ height = display->height; ++ } ++ ++ vmem_size = display->width * display->height * bpp / 8; ++ 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 = pdata->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 = pdata->bgr; ++ par->startbyte = pdata->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) { ++ if (fbtft_gamma_parse_str(par, ++ par->gamma.curves, gamma, strlen(gamma))) ++ goto alloc_fail; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ 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); ++ ++ 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); ++ ret = unregister_framebuffer(fb_info); ++ return ret; ++} ++EXPORT_SYMBOL(fbtft_unregister_framebuffer); ++ ++#ifdef CONFIG_OF ++/** ++ * fbtft_init_display_dt() - Device Tree init_display() function ++ * @par: Driver data ++ * ++ * Return: 0 if successful, negative if error ++ */ ++static int fbtft_init_display_dt(struct fbtft_par *par) ++{ ++ struct device_node *node = par->info->device->of_node; ++ struct property *prop; ++ const __be32 *p; ++ u32 val; ++ int buf[64], i, j; ++ char msg[128]; ++ char str[16]; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ if (!node) ++ return -EINVAL; ++ ++ prop = of_find_property(node, "init", NULL); ++ p = of_prop_next_u32(prop, NULL, &val); ++ if (!p) ++ return -EINVAL; ++ while (p) { ++ if (val & FBTFT_OF_INIT_CMD) { ++ val &= 0xFFFF; ++ i = 0; ++ while (p && !(val & 0xFFFF0000)) { ++ if (i > 63) { ++ dev_err(par->info->device, ++ "%s: Maximum register values exceeded\n", ++ __func__); ++ return -EINVAL; ++ } ++ buf[i++] = val; ++ p = of_prop_next_u32(prop, p, &val); ++ } ++ /* make debug message */ ++ msg[0] = '\0'; ++ for (j = 0; j < i; j++) { ++ snprintf(str, 128, " %02X", buf[j]); ++ strcat(msg, str); ++ } ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: write_register:%s\n", msg); ++ ++ par->fbtftops.write_register(par, i, ++ 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]); ++ } else if (val & FBTFT_OF_INIT_DELAY) { ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: msleep(%u)\n", val & 0xFFFF); ++ msleep(val & 0xFFFF); ++ p = of_prop_next_u32(prop, p, &val); ++ } else { ++ dev_err(par->info->device, "illegal init value 0x%X\n", ++ val); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++/** ++ * 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->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; ++} ++ ++#ifdef CONFIG_OF ++/* returns 0 if the property is not present */ ++static u32 fbtft_of_value(struct device_node *node, const char *propname) ++{ ++ int ret; ++ u32 val = 0; ++ ++ ret = of_property_read_u32(node, propname, &val); ++ if (ret == 0) ++ pr_info("%s: %s = %u\n", __func__, propname, val); ++ ++ return val; ++} ++ ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ struct fbtft_platform_data *pdata; ++ ++ if (!node) { ++ dev_err(dev, "Missing platform data or DT\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ pdata->display.width = fbtft_of_value(node, "width"); ++ pdata->display.height = fbtft_of_value(node, "height"); ++ pdata->display.regwidth = fbtft_of_value(node, "regwidth"); ++ pdata->display.buswidth = fbtft_of_value(node, "buswidth"); ++ pdata->display.backlight = fbtft_of_value(node, "backlight"); ++ pdata->display.bpp = fbtft_of_value(node, "bpp"); ++ pdata->display.debug = fbtft_of_value(node, "debug"); ++ pdata->rotate = fbtft_of_value(node, "rotate"); ++ pdata->bgr = of_property_read_bool(node, "bgr"); ++ pdata->fps = fbtft_of_value(node, "fps"); ++ pdata->txbuflen = fbtft_of_value(node, "txbuflen"); ++ pdata->startbyte = fbtft_of_value(node, "startbyte"); ++ of_property_read_string(node, "gamma", (const char **)&pdata->gamma); ++ ++ if (of_find_property(node, "led-gpios", NULL)) ++ pdata->display.backlight = 1; ++ if (of_find_property(node, "init", NULL)) ++ pdata->display.fbtftops.init_display = fbtft_init_display_dt; ++ pdata->display.fbtftops.request_gpios = fbtft_request_gpios_dt; ++ ++ return pdata; ++} ++#else ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ dev_err(dev, "Missing platform data\n"); ++ return ERR_PTR(-EINVAL); ++} ++#endif ++ ++/** ++ * 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) { ++ pdata = fbtft_probe_dt(dev); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ dev->platform_data = pdata; ++ } ++ ++ info = fbtft_framebuffer_alloc(display, dev); ++ if (!info) ++ return -ENOMEM; ++ ++ par = info->par; ++ par->spi = sdev; ++ par->pdev = pdev; ++ ++ if (display->buswidth == 0) { ++ dev_err(dev, "buswidth is not set\n"); ++ return -EINVAL; ++ } ++ ++ /* 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 */ ++ 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..45f8de3 +--- /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, 0660, show_gamma_curve, store_gamma_curve), ++}; ++ ++ ++void fbtft_expand_debug_value(unsigned long *debug) ++{ ++ switch (*debug & 0b111) { ++ case 1: ++ *debug |= DEBUG_LEVEL_1; ++ break; ++ case 2: ++ *debug |= DEBUG_LEVEL_2; ++ break; ++ case 3: ++ *debug |= DEBUG_LEVEL_3; ++ break; ++ case 4: ++ *debug |= DEBUG_LEVEL_4; ++ break; ++ case 5: ++ *debug |= DEBUG_LEVEL_5; ++ break; ++ case 6: ++ *debug |= DEBUG_LEVEL_6; ++ break; ++ case 7: ++ *debug = 0xFFFFFFFF; ++ break; ++ } ++} ++ ++static ssize_t store_debug(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ int ret; ++ ++ ret = kstrtoul(buf, 10, &par->debug); ++ if (ret) ++ return ret; ++ fbtft_expand_debug_value(&par->debug); ++ ++ return count; ++} ++ ++static ssize_t show_debug(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ ++ return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug); ++} ++ ++static struct device_attribute debug_device_attr = \ ++ __ATTR(debug, 0660, show_debug, store_debug); ++ ++ ++void fbtft_sysfs_init(struct fbtft_par *par) ++{ ++ device_create_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_create_file(par->info->dev, &gamma_device_attrs[0]); ++} ++ ++void fbtft_sysfs_exit(struct fbtft_par *par) ++{ ++ device_remove_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_remove_file(par->info->dev, &gamma_device_attrs[0]); ++} +diff --git a/drivers/video/fbtft/fbtft.h b/drivers/video/fbtft/fbtft.h +new file mode 100644 +index 0000000..0dbf3f9 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft.h +@@ -0,0 +1,447 @@ ++/* ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __LINUX_FBTFT_H ++#define __LINUX_FBTFT_H ++ ++#include ++#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 ++ ++#define FBTFT_OF_INIT_CMD BIT(24) ++#define FBTFT_OF_INIT_DELAY BIT(25) ++ ++/** ++ * struct fbtft_gpio - Structure that holds one pinname to gpio mapping ++ * @name: pinname (reset, dc, etc.) ++ * @gpio: GPIO number ++ * ++ */ ++struct fbtft_gpio { ++ char name[FBTFT_GPIO_NAME_SIZE]; ++ unsigned gpio; ++}; ++ ++struct fbtft_par; ++ ++/** ++ * struct fbtft_ops - FBTFT operations structure ++ * @write: Writes to interface bus ++ * @read: Reads from interface bus ++ * @write_vmem: Writes video memory to display ++ * @write_reg: Writes to controller register ++ * @set_addr_win: Set the GRAM update window ++ * @reset: Reset the LCD controller ++ * @mkdirty: Marks display lines for update ++ * @update_display: Updates the display ++ * @init_display: Initializes the display ++ * @blank: Blank the display (optional) ++ * @request_gpios_match: Do pinname to gpio matching ++ * @request_gpios: Request gpios from the kernel ++ * @free_gpios: Free previously requested gpios ++ * @verify_gpios: Verify that necessary gpios is present (optional) ++ * @register_backlight: Used to register backlight device (optional) ++ * @unregister_backlight: Unregister backlight device (optional) ++ * @set_var: Configure LCD with values from variables like @rotate and @bgr ++ * (optional) ++ * @set_gamma: Set Gamma curve (optional) ++ * ++ * Most of these operations have default functions assigned to them in ++ * fbtft_framebuffer_alloc() ++ */ ++struct fbtft_ops { ++ int (*write)(struct fbtft_par *par, void *buf, size_t len); ++ int (*read)(struct fbtft_par *par, void *buf, size_t len); ++ int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len); ++ void (*write_register)(struct fbtft_par *par, int len, ...); ++ ++ void (*set_addr_win)(struct fbtft_par *par, ++ int xs, int ys, int xe, int ye); ++ void (*reset)(struct fbtft_par *par); ++ void (*mkdirty)(struct fb_info *info, int from, int to); ++ void (*update_display)(struct fbtft_par *par, ++ unsigned start_line, unsigned end_line); ++ int (*init_display)(struct fbtft_par *par); ++ int (*blank)(struct fbtft_par *par, bool on); ++ ++ unsigned long (*request_gpios_match)(struct fbtft_par *par, ++ const struct fbtft_gpio *gpio); ++ int (*request_gpios)(struct fbtft_par *par); ++ int (*verify_gpios)(struct fbtft_par *par); ++ ++ void (*register_backlight)(struct fbtft_par *par); ++ void (*unregister_backlight)(struct fbtft_par *par); ++ ++ int (*set_var)(struct fbtft_par *par); ++ int (*set_gamma)(struct fbtft_par *par, unsigned long *curves); ++}; ++ ++/** ++ * struct fbtft_display - Describes the display properties ++ * @width: Width of display in pixels ++ * @height: Height of display in pixels ++ * @regwidth: LCD Controller Register width in bits ++ * @buswidth: Display interface bus width in bits ++ * @backlight: Backlight type. ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @bpp: Bits per pixel ++ * @fps: Frames per second ++ * @txbuflen: Size of transmit buffer ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma: String representation of Gamma curve(s) ++ * @gamma_num: Number of Gamma curves ++ * @gamma_len: Number of values per Gamma curve ++ * @debug: Initial debug value ++ * ++ * This structure is not stored by FBTFT except for init_sequence. ++ */ ++struct fbtft_display { ++ unsigned width; ++ unsigned height; ++ unsigned regwidth; ++ unsigned buswidth; ++ unsigned backlight; ++ struct fbtft_ops fbtftops; ++ unsigned bpp; ++ unsigned fps; ++ int txbuflen; ++ int *init_sequence; ++ char *gamma; ++ int gamma_num; ++ int gamma_len; ++ unsigned long debug; ++}; ++ ++/** ++ * struct fbtft_platform_data - Passes display specific data to the driver ++ * @display: Display properties ++ * @gpios: Pointer to an array of piname to gpio mappings ++ * @rotate: Display rotation angle ++ * @bgr: LCD Controller BGR bit ++ * @fps: Frames per second (this will go away, use @fps in @fbtft_display) ++ * @txbuflen: Size of transmit buffer ++ * @startbyte: When set, enables use of Startbyte in transfers ++ * @gamma: String representation of Gamma curve(s) ++ * @extra: A way to pass extra info ++ */ ++struct fbtft_platform_data { ++ struct fbtft_display display; ++ const struct fbtft_gpio *gpios; ++ unsigned rotate; ++ bool bgr; ++ unsigned fps; ++ int txbuflen; ++ u8 startbyte; ++ char *gamma; ++ void *extra; ++}; ++ ++/** ++ * struct fbtft_par - Main FBTFT data structure ++ * ++ * This structure holds all relevant data to operate the display ++ * ++ * See sourcefile for documentation since nested structs is not ++ * supported by kernel-doc. ++ * ++ */ ++/* @spi: Set if it is a SPI device ++ * @pdev: Set if it is a platform device ++ * @info: Pointer to framebuffer fb_info structure ++ * @pdata: Pointer to platform data ++ * @ssbuf: Not used ++ * @pseudo_palette: Used by fb_set_colreg() ++ * @txbuf.buf: Transmit buffer ++ * @txbuf.len: Transmit buffer length ++ * @buf: Small buffer used when writing init data over SPI ++ * @startbyte: Used by some controllers when in SPI mode. ++ * Format: 6 bit Device id + RS bit + RW bit ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @dirty_lock: Protects dirty_lines_start and dirty_lines_end ++ * @dirty_lines_start: Where to begin updating display ++ * @dirty_lines_end: Where to end updating display ++ * @gpio.reset: GPIO used to reset display ++ * @gpio.dc: Data/Command signal, also known as RS ++ * @gpio.rd: Read latching signal ++ * @gpio.wr: Write latching signal ++ * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch ++ * @gpio.cs: LCD Chip Select with parallel interface bus ++ * @gpio.db[16]: Parallel databus ++ * @gpio.led[16]: Led control signals ++ * @gpio.aux[16]: Auxillary signals, not used by core ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma.lock: Mutex for Gamma curve locking ++ * @gamma.curves: Pointer to Gamma curve array ++ * @gamma.num_values: Number of values per Gamma curve ++ * @gamma.num_curves: Number of Gamma curves ++ * @debug: Pointer to debug value ++ * @current_debug: ++ * @first_update_done: Used to only time the first display update ++ * @update_time: Used to calculate 'fps' in debug output ++ * @bgr: BGR mode/\n ++ * @extra: Extra info needed by driver ++ */ ++struct fbtft_par { ++ struct spi_device *spi; ++ struct platform_device *pdev; ++ struct fb_info *info; ++ struct fbtft_platform_data *pdata; ++ u16 *ssbuf; ++ u32 pseudo_palette[16]; ++ struct { ++ void *buf; ++ dma_addr_t dma; ++ size_t len; ++ } txbuf; ++ u8 *buf; ++ u8 startbyte; ++ struct fbtft_ops fbtftops; ++ spinlock_t dirty_lock; ++ unsigned dirty_lines_start; ++ unsigned dirty_lines_end; ++ struct { ++ int reset; ++ int dc; ++ int rd; ++ int wr; ++ int latch; ++ int cs; ++ int db[16]; ++ int led[16]; ++ int aux[16]; ++ } gpio; ++ int *init_sequence; ++ struct { ++ struct mutex lock; ++ unsigned long *curves; ++ int num_values; ++ int num_curves; ++ } gamma; ++ unsigned long debug; ++ bool first_update_done; ++ struct timespec update_time; ++ bool bgr; ++ void *extra; ++}; ++ ++#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) ++ ++#define write_reg(par, ...) \ ++do { \ ++ par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \ ++} while (0) ++ ++/* fbtft-core.c */ ++extern void fbtft_dbg_hex(const struct device *dev, ++ int groupsize, void *buf, size_t len, const char *fmt, ...); ++extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, ++ struct device *dev); ++extern void fbtft_framebuffer_release(struct fb_info *info); ++extern int fbtft_register_framebuffer(struct fb_info *fb_info); ++extern int fbtft_unregister_framebuffer(struct fb_info *fb_info); ++extern void fbtft_register_backlight(struct fbtft_par *par); ++extern void fbtft_unregister_backlight(struct fbtft_par *par); ++extern int fbtft_init_display(struct fbtft_par *par); ++extern int fbtft_probe_common(struct fbtft_display *display, ++ struct spi_device *sdev, struct platform_device *pdev); ++extern int fbtft_remove_common(struct device *dev, struct fb_info *info); ++ ++/* fbtft-io.c */ ++extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_spi_emulate_9(struct fbtft_par *par, ++ void *buf, size_t len); ++extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, ++ void *buf, size_t len); ++ ++/* fbtft-bus.c */ ++extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len); ++extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...); ++ ++ ++#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \ ++ \ ++static int fbtft_driver_probe_spi(struct spi_device *spi) \ ++{ \ ++ return fbtft_probe_common(_display, spi, NULL); \ ++} \ ++ \ ++static int fbtft_driver_remove_spi(struct spi_device *spi) \ ++{ \ ++ struct fb_info *info = spi_get_drvdata(spi); \ ++ \ ++ return fbtft_remove_common(&spi->dev, info); \ ++} \ ++ \ ++static int fbtft_driver_probe_pdev(struct platform_device *pdev) \ ++{ \ ++ return fbtft_probe_common(_display, NULL, pdev); \ ++} \ ++ \ ++static int fbtft_driver_remove_pdev(struct platform_device *pdev) \ ++{ \ ++ struct fb_info *info = platform_get_drvdata(pdev); \ ++ \ ++ return fbtft_remove_common(&pdev->dev, info); \ ++} \ ++ \ ++static const struct of_device_id dt_ids[] = { \ ++ { .compatible = _compatible }, \ ++ {}, \ ++}; \ ++ \ ++MODULE_DEVICE_TABLE(of, dt_ids); \ ++ \ ++ \ ++static struct spi_driver fbtft_driver_spi_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_spi, \ ++ .remove = fbtft_driver_remove_spi, \ ++}; \ ++ \ ++static struct platform_driver fbtft_driver_platform_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_pdev, \ ++ .remove = fbtft_driver_remove_pdev, \ ++}; \ ++ \ ++static int __init fbtft_driver_module_init(void) \ ++{ \ ++ int ret; \ ++ \ ++ ret = spi_register_driver(&fbtft_driver_spi_driver); \ ++ if (ret < 0) \ ++ return ret; \ ++ return platform_driver_register(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++static void __exit fbtft_driver_module_exit(void) \ ++{ \ ++ spi_unregister_driver(&fbtft_driver_spi_driver); \ ++ platform_driver_unregister(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++module_init(fbtft_driver_module_init); \ ++module_exit(fbtft_driver_module_exit); ++ ++ ++/* Debug macros */ ++ ++/* shorthand debug levels */ ++#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS ++#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE) ++#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS) ++#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK) ++#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY) ++#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5) ++#define DEBUG_LEVEL_7 0xFFFFFFFF ++ ++#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3) ++#define DEBUG_TIME_FIRST_UPDATE (1<<4) ++#define DEBUG_TIME_EACH_UPDATE (1<<5) ++#define DEBUG_DEFERRED_IO (1<<6) ++#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7) ++ ++/* fbops */ ++#define DEBUG_FB_READ (1<<8) ++#define DEBUG_FB_WRITE (1<<9) ++#define DEBUG_FB_FILLRECT (1<<10) ++#define DEBUG_FB_COPYAREA (1<<11) ++#define DEBUG_FB_IMAGEBLIT (1<<12) ++#define DEBUG_FB_SETCOLREG (1<<13) ++#define DEBUG_FB_BLANK (1<<14) ++ ++#define DEBUG_SYSFS (1<<16) ++ ++/* fbtftops */ ++#define DEBUG_BACKLIGHT (1<<17) ++#define DEBUG_READ (1<<18) ++#define DEBUG_WRITE (1<<19) ++#define DEBUG_WRITE_VMEM (1<<20) ++#define DEBUG_WRITE_REGISTER (1<<21) ++#define DEBUG_SET_ADDR_WIN (1<<22) ++#define DEBUG_RESET (1<<23) ++#define DEBUG_MKDIRTY (1<<24) ++#define DEBUG_UPDATE_DISPLAY (1<<25) ++#define DEBUG_INIT_DISPLAY (1<<26) ++#define DEBUG_BLANK (1<<27) ++#define DEBUG_REQUEST_GPIOS (1<<28) ++#define DEBUG_FREE_GPIOS (1<<29) ++#define DEBUG_REQUEST_GPIOS_MATCH (1<<30) ++#define DEBUG_VERIFY_GPIOS (1<<31) ++ ++ ++#define fbtft_init_dbg(dev, format, arg...) \ ++do { \ ++ if (unlikely((dev)->platform_data && \ ++ (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg(level, par, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(par->info->device, format, ##arg); \ ++} while (0) ++ ++#define fbtft_dev_dbg(level, par, dev, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \ ++} while (0) ++ ++#endif /* __LINUX_FBTFT_H */ +diff --git a/drivers/video/fbtft/fbtft_device.c b/drivers/video/fbtft/fbtft_device.c +new file mode 100644 +index 0000000..b9f4c30 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft_device.c +@@ -0,0 +1,1444 @@ ++/* ++ * ++ * 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 }; ++ ++static int waveshare32b_init_sequence[] = { ++ -1,0xCB,0x39,0x2C,0x00,0x34,0x02,-1,0xCF,0x00,0xC1,0x30, ++ -1,0xE8,0x85,0x00,0x78,-1,0xEA,0x00,0x00,-1,0xED,0x64,0x03,0x12,0x81, ++ -1,0xF7,0x20,-1,0xC0,0x23,-1,0xC1,0x10,-1,0xC5,0x3e,0x28,-1,0xC7,0x86, ++ -1,0x36,0x28,-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,120,-1,0x29,-1,0x2c,-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 = "adafruit28", ++ .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 = "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 = "agm1264k-fl", ++ .pdev = &(struct platform_device) { ++ .name = "fb_agm1264k-fl", ++ .id = 0, ++ .dev = { ++ .release = fbtft_device_pdev_release, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = FBTFT_ONBOARD_BACKLIGHT, ++ }, ++ .gpios = (const struct fbtft_gpio []) { ++ {}, ++ }, ++ }, ++ } ++ } ++ }, { ++ .name = "dogs102", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_uc1701", ++ .max_speed_hz = 8000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 13 }, ++ { "dc", 6 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = "ili9481", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 32000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .regwidth = 16, ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 25 }, ++ { "dc", 24 }, ++ { "led", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = { ++ .regwidth = 16, ++ .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 = "tontec35_9481", /* boards before 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "led_", 18 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .name = "tontec35_9486", /* boards after 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9486", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "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 = "waveshare32b", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9340", ++ .max_speed_hz = 48000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ .init_sequence = waveshare32b_init_sequence, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 27 }, ++ { "dc", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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/odroid-default/patch-3.10.92-93.patch b/patch/kernel/odroid-default/patch-3.10.92-93.patch new file mode 100644 index 000000000..b4317e6ab --- /dev/null +++ b/patch/kernel/odroid-default/patch-3.10.92-93.patch @@ -0,0 +1,645 @@ +diff --git a/Makefile b/Makefile +index 25701b67bb6d..6944160a5631 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 3 + PATCHLEVEL = 10 +-SUBLEVEL = 92 ++SUBLEVEL = 93 + EXTRAVERSION = + NAME = TOSSUG Baby Fish + +diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c +index 048334bb2651..d25459ff57fc 100644 +--- a/arch/arm64/kernel/stacktrace.c ++++ b/arch/arm64/kernel/stacktrace.c +@@ -48,11 +48,7 @@ int unwind_frame(struct stackframe *frame) + + frame->sp = fp + 0x10; + frame->fp = *(unsigned long *)(fp); +- /* +- * -4 here because we care about the PC at time of bl, +- * not where the return will go. +- */ +- frame->pc = *(unsigned long *)(fp + 8) - 4; ++ frame->pc = *(unsigned long *)(fp + 8); + + return 0; + } +diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c +index f956a2f84a15..8d3722af6187 100644 +--- a/arch/powerpc/kernel/rtas.c ++++ b/arch/powerpc/kernel/rtas.c +@@ -1041,6 +1041,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ++ if (!rtas.entry) ++ return -EINVAL; ++ + if (copy_from_user(&args, uargs, 3 * sizeof(u32)) != 0) + return -EFAULT; + +diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c +index 511630db00a8..91cbe75a91d5 100644 +--- a/arch/x86/xen/enlighten.c ++++ b/arch/x86/xen/enlighten.c +@@ -33,7 +33,7 @@ + #include + #include + +-#ifdef CONFIG_KEXEC_CORE ++#ifdef CONFIG_KEXEC + #include + #endif + +@@ -1748,7 +1748,7 @@ static struct notifier_block xen_hvm_cpu_notifier __cpuinitdata = { + .notifier_call = xen_hvm_cpu_notify, + }; + +-#ifdef CONFIG_KEXEC_CORE ++#ifdef CONFIG_KEXEC + static void xen_hvm_shutdown(void) + { + native_machine_shutdown(); +@@ -1777,7 +1777,7 @@ static void __init xen_hvm_guest_init(void) + x86_init.irqs.intr_init = xen_init_IRQ; + xen_hvm_init_time_ops(); + xen_hvm_init_mmu_ops(); +-#ifdef CONFIG_KEXEC_CORE ++#ifdef CONFIG_KEXEC + machine_ops.shutdown = xen_hvm_shutdown; + machine_ops.crash_shutdown = xen_hvm_crash_shutdown; + #endif +diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c +index 7d4a8d28277e..ebcec7439a1a 100644 +--- a/crypto/ablkcipher.c ++++ b/crypto/ablkcipher.c +@@ -700,7 +700,7 @@ struct crypto_ablkcipher *crypto_alloc_ablkcipher(const char *alg_name, + err: + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +diff --git a/crypto/algapi.c b/crypto/algapi.c +index 00d8d939733b..daf2f653b131 100644 +--- a/crypto/algapi.c ++++ b/crypto/algapi.c +@@ -325,7 +325,7 @@ static void crypto_wait_for_test(struct crypto_larval *larval) + crypto_alg_tested(larval->alg.cra_driver_name, 0); + } + +- err = wait_for_completion_interruptible(&larval->completion); ++ err = wait_for_completion_killable(&larval->completion); + WARN_ON(err); + + out: +diff --git a/crypto/api.c b/crypto/api.c +index 335abea14f19..36a0d4602eba 100644 +--- a/crypto/api.c ++++ b/crypto/api.c +@@ -172,7 +172,7 @@ static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg) + struct crypto_larval *larval = (void *)alg; + long timeout; + +- timeout = wait_for_completion_interruptible_timeout( ++ timeout = wait_for_completion_killable_timeout( + &larval->completion, 60 * HZ); + + alg = larval->adult; +@@ -435,7 +435,7 @@ struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask) + err: + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +@@ -552,7 +552,7 @@ void *crypto_alloc_tfm(const char *alg_name, + err: + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c +index 43665d0d0905..c7666f401381 100644 +--- a/crypto/crypto_user.c ++++ b/crypto/crypto_user.c +@@ -361,7 +361,7 @@ static struct crypto_alg *crypto_user_aead_alg(const char *name, u32 type, + err = PTR_ERR(alg); + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c +index 2fa22c24fa5d..85b31ba9d503 100644 +--- a/drivers/block/rbd.c ++++ b/drivers/block/rbd.c +@@ -93,6 +93,8 @@ static int atomic_dec_return_safe(atomic_t *v) + + #define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */ + ++#define RBD_MAX_PARENT_CHAIN_LEN 16 ++ + #define RBD_SNAP_DEV_NAME_PREFIX "snap_" + #define RBD_MAX_SNAP_NAME_LEN \ + (NAME_MAX - (sizeof (RBD_SNAP_DEV_NAME_PREFIX) - 1)) +@@ -394,7 +396,7 @@ static ssize_t rbd_add(struct bus_type *bus, const char *buf, + size_t count); + static ssize_t rbd_remove(struct bus_type *bus, const char *buf, + size_t count); +-static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping); ++static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth); + static void rbd_spec_put(struct rbd_spec *spec); + + static struct bus_attribute rbd_bus_attrs[] = { +@@ -3453,6 +3455,9 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) + blk_queue_io_opt(q, segment_size); + + blk_queue_merge_bvec(q, rbd_merge_bvec); ++ if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC)) ++ q->backing_dev_info.capabilities |= BDI_CAP_STABLE_WRITES; ++ + disk->queue = q; + + q->queuedata = rbd_dev; +@@ -4828,44 +4833,50 @@ out_err: + return ret; + } + +-static int rbd_dev_probe_parent(struct rbd_device *rbd_dev) ++/* ++ * @depth is rbd_dev_image_probe() -> rbd_dev_probe_parent() -> ++ * rbd_dev_image_probe() recursion depth, which means it's also the ++ * length of the already discovered part of the parent chain. ++ */ ++static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth) + { + struct rbd_device *parent = NULL; +- struct rbd_spec *parent_spec; +- struct rbd_client *rbdc; + int ret; + + if (!rbd_dev->parent_spec) + return 0; +- /* +- * We need to pass a reference to the client and the parent +- * spec when creating the parent rbd_dev. Images related by +- * parent/child relationships always share both. +- */ +- parent_spec = rbd_spec_get(rbd_dev->parent_spec); +- rbdc = __rbd_get_client(rbd_dev->rbd_client); + +- ret = -ENOMEM; +- parent = rbd_dev_create(rbdc, parent_spec); +- if (!parent) ++ if (++depth > RBD_MAX_PARENT_CHAIN_LEN) { ++ pr_info("parent chain is too long (%d)\n", depth); ++ ret = -EINVAL; + goto out_err; ++ } + +- ret = rbd_dev_image_probe(parent, false); ++ parent = rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec); ++ if (!parent) { ++ ret = -ENOMEM; ++ goto out_err; ++ } ++ ++ /* ++ * Images related by parent/child relationships always share ++ * rbd_client and spec/parent_spec, so bump their refcounts. ++ */ ++ __rbd_get_client(rbd_dev->rbd_client); ++ rbd_spec_get(rbd_dev->parent_spec); ++ ++ ret = rbd_dev_image_probe(parent, depth); + if (ret < 0) + goto out_err; ++ + rbd_dev->parent = parent; + atomic_set(&rbd_dev->parent_ref, 1); +- + return 0; ++ + out_err: +- if (parent) { +- rbd_dev_unparent(rbd_dev); ++ rbd_dev_unparent(rbd_dev); ++ if (parent) + rbd_dev_destroy(parent); +- } else { +- rbd_put_client(rbdc); +- rbd_spec_put(parent_spec); +- } +- + return ret; + } + +@@ -4971,7 +4982,7 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev) + * parent), initiate a watch on its header object before using that + * object to get detailed information about the rbd image. + */ +-static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) ++static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) + { + int ret; + int tmp; +@@ -4992,7 +5003,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + if (ret) + goto err_out_format; + +- if (mapping) { ++ if (!depth) { + ret = rbd_dev_header_watch_sync(rbd_dev, true); + if (ret) + goto out_header_name; +@@ -5009,7 +5020,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + if (ret) + goto err_out_probe; + +- ret = rbd_dev_probe_parent(rbd_dev); ++ ret = rbd_dev_probe_parent(rbd_dev, depth); + if (ret) + goto err_out_probe; + +@@ -5020,7 +5031,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + err_out_probe: + rbd_dev_unprobe(rbd_dev); + err_out_watch: +- if (mapping) { ++ if (!depth) { + tmp = rbd_dev_header_watch_sync(rbd_dev, false); + if (tmp) + rbd_warn(rbd_dev, "unable to tear down " +@@ -5091,7 +5102,7 @@ static ssize_t rbd_add(struct bus_type *bus, + rbdc = NULL; /* rbd_dev now owns this */ + spec = NULL; /* rbd_dev now owns this */ + +- rc = rbd_dev_image_probe(rbd_dev, true); ++ rc = rbd_dev_image_probe(rbd_dev, 0); + if (rc < 0) + goto err_out_rbd_dev; + +diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c +index ddd9a098bc67..ea7ed8600a7c 100644 +--- a/drivers/block/xen-blkfront.c ++++ b/drivers/block/xen-blkfront.c +@@ -1590,7 +1590,8 @@ static void blkback_changed(struct xenbus_device *dev, + break; + /* Missed the backend's Closing state -- fallthrough */ + case XenbusStateClosing: +- blkfront_closing(info); ++ if (info) ++ blkfront_closing(info); + break; + } + } +diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c +index 5bccf31cc974..4d41739fb50a 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_gem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c +@@ -177,11 +177,12 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_vma *vma; + +- if (nvbo->bo.mem.mem_type == TTM_PL_TT) ++ if (is_power_of_2(nvbo->valid_domains)) ++ rep->domain = nvbo->valid_domains; ++ else if (nvbo->bo.mem.mem_type == TTM_PL_TT) + rep->domain = NOUVEAU_GEM_DOMAIN_GART; + else + rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; +- + rep->offset = nvbo->bo.offset; + if (cli->base.vm) { + vma = nouveau_bo_vma_find(nvbo, cli->base.vm); +diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c +index 784b97cb05b0..c410217fbe89 100644 +--- a/drivers/infiniband/core/cm.c ++++ b/drivers/infiniband/core/cm.c +@@ -857,6 +857,11 @@ retest: + case IB_CM_SIDR_REQ_RCVD: + spin_unlock_irq(&cm_id_priv->lock); + cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT); ++ spin_lock_irq(&cm.lock); ++ if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) ++ rb_erase(&cm_id_priv->sidr_id_node, ++ &cm.remote_sidr_table); ++ spin_unlock_irq(&cm.lock); + break; + case IB_CM_REQ_SENT: + ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg); +@@ -3093,7 +3098,10 @@ int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id, + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + spin_lock_irqsave(&cm.lock, flags); +- rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); ++ if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) { ++ rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); ++ RB_CLEAR_NODE(&cm_id_priv->sidr_id_node); ++ } + spin_unlock_irqrestore(&cm.lock, flags); + return 0; + +diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c +index dfb401cba733..6bde2a124c72 100644 +--- a/drivers/iommu/amd_iommu.c ++++ b/drivers/iommu/amd_iommu.c +@@ -2106,8 +2106,8 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats) + static void clear_dte_entry(u16 devid) + { + /* remove entry from the device table seen by the hardware */ +- amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV; +- amd_iommu_dev_table[devid].data[1] = 0; ++ amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV; ++ amd_iommu_dev_table[devid].data[1] &= DTE_FLAG_MASK; + + amd_iommu_apply_erratum_63(devid); + } +diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h +index 0285a215df16..7570f45fce8d 100644 +--- a/drivers/iommu/amd_iommu_types.h ++++ b/drivers/iommu/amd_iommu_types.h +@@ -282,6 +282,7 @@ + #define IOMMU_PTE_IR (1ULL << 61) + #define IOMMU_PTE_IW (1ULL << 62) + ++#define DTE_FLAG_MASK (0x3ffULL << 32) + #define DTE_FLAG_IOTLB (0x01UL << 32) + #define DTE_FLAG_GV (0x01ULL << 55) + #define DTE_GLX_SHIFT (56) +diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c +index 7c0d75547ccf..92cd09f3c69b 100644 +--- a/drivers/md/persistent-data/dm-btree-remove.c ++++ b/drivers/md/persistent-data/dm-btree-remove.c +@@ -301,11 +301,16 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, + { + int s; + uint32_t max_entries = le32_to_cpu(left->header.max_entries); +- unsigned target = (nr_left + nr_center + nr_right) / 3; +- BUG_ON(target > max_entries); ++ unsigned total = nr_left + nr_center + nr_right; ++ unsigned target_right = total / 3; ++ unsigned remainder = (target_right * 3) != total; ++ unsigned target_left = target_right + remainder; ++ ++ BUG_ON(target_left > max_entries); ++ BUG_ON(target_right > max_entries); + + if (nr_left < nr_right) { +- s = nr_left - target; ++ s = nr_left - target_left; + + if (s < 0 && nr_center < -s) { + /* not enough in central node */ +@@ -316,10 +321,10 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, + } else + shift(left, center, s); + +- shift(center, right, target - nr_right); ++ shift(center, right, target_right - nr_right); + + } else { +- s = target - nr_right; ++ s = target_right - nr_right; + if (s > 0 && nr_center < s) { + /* not enough in central node */ + shift(center, right, nr_center); +@@ -329,7 +334,7 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, + } else + shift(center, right, s); + +- shift(left, center, nr_left - target); ++ shift(left, center, nr_left - target_left); + } + + *key_ptr(parent, c->index) = center->keys[0]; +diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c +index 79233b051da0..b53669404cb5 100644 +--- a/drivers/md/persistent-data/dm-btree.c ++++ b/drivers/md/persistent-data/dm-btree.c +@@ -507,7 +507,7 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key) + + r = new_block(s->info, &right); + if (r < 0) { +- /* FIXME: put left */ ++ unlock_block(s->info, left); + return r; + } + +diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c +index 72141ee60705..63d42ae56a1c 100644 +--- a/drivers/md/raid1.c ++++ b/drivers/md/raid1.c +@@ -2147,7 +2147,7 @@ static int narrow_write_error(struct r1bio *r1_bio, int i) + md_trim_bio(wbio, sector - r1_bio->sector, sectors); + wbio->bi_sector += rdev->data_offset; + wbio->bi_bdev = rdev->bdev; +- if (submit_bio_wait(WRITE, wbio) == 0) ++ if (submit_bio_wait(WRITE, wbio) < 0) + /* failure! */ + ok = rdev_set_badblocks(rdev, sector, + sectors, 0) +diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c +index 5b2a1eaea34d..f53f4f895502 100644 +--- a/drivers/md/raid10.c ++++ b/drivers/md/raid10.c +@@ -2597,7 +2597,7 @@ static int narrow_write_error(struct r10bio *r10_bio, int i) + choose_data_offset(r10_bio, rdev) + + (sector - r10_bio->sector)); + wbio->bi_bdev = rdev->bdev; +- if (submit_bio_wait(WRITE, wbio) == 0) ++ if (submit_bio_wait(WRITE, wbio) < 0) + /* Failure! */ + ok = rdev_set_badblocks(rdev, sector, + sectors, 0) +diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c +index bd126c25a727..3f37e464a599 100644 +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -819,6 +819,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) + hw->max_rate_tries = 10; + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); ++ hw->extra_tx_headroom = 4; + + hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; + hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; +diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c +index 54f553380aa8..54308dfde13d 100644 +--- a/drivers/net/wireless/iwlwifi/dvm/lib.c ++++ b/drivers/net/wireless/iwlwifi/dvm/lib.c +@@ -1023,7 +1023,7 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); +- aes_sc->pn = cpu_to_le64( ++ aes_sc[i].pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | +diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c +index 16bbdcc8627a..2e95b419a109 100644 +--- a/drivers/net/wireless/iwlwifi/mvm/d3.c ++++ b/drivers/net/wireless/iwlwifi/mvm/d3.c +@@ -295,12 +295,12 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); +- aes_sc->pn = cpu_to_le64((u64)pn[5] | +- ((u64)pn[4] << 8) | +- ((u64)pn[3] << 16) | +- ((u64)pn[2] << 24) | +- ((u64)pn[1] << 32) | +- ((u64)pn[0] << 40)); ++ aes_sc[i].pn = cpu_to_le64((u64)pn[5] | ++ ((u64)pn[4] << 8) | ++ ((u64)pn[3] << 16) | ++ ((u64)pn[2] << 24) | ++ ((u64)pn[1] << 32) | ++ ((u64)pn[0] << 40)); + } + data->use_rsc_tsc = true; + break; +diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c +index fa50c7dc3d3e..2da1959ff2f6 100644 +--- a/drivers/scsi/mvsas/mv_sas.c ++++ b/drivers/scsi/mvsas/mv_sas.c +@@ -987,6 +987,8 @@ static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc) + static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task, + struct mvs_slot_info *slot, u32 slot_idx) + { ++ if (!slot) ++ return; + if (!slot->task) + return; + if (!sas_protocol_ata(task->task_proto)) +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +index 4ba6974dd4b6..98e3b20c6058 100644 +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -2348,6 +2348,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, + u32 trb_comp_code; + int ret = 0; + int td_num = 0; ++ bool handling_skipped_tds = false; + + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); + xdev = xhci->devs[slot_id]; +@@ -2481,6 +2482,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, + ep->skip = true; + xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); + goto cleanup; ++ case COMP_PING_ERR: ++ ep->skip = true; ++ xhci_dbg(xhci, "No Ping response error, Skip one Isoc TD\n"); ++ goto cleanup; + default: + if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { + status = 0; +@@ -2612,13 +2617,18 @@ static int handle_tx_event(struct xhci_hcd *xhci, + ep, &status); + + cleanup: ++ ++ ++ handling_skipped_tds = ep->skip && ++ trb_comp_code != COMP_MISSED_INT && ++ trb_comp_code != COMP_PING_ERR; ++ + /* +- * Do not update event ring dequeue pointer if ep->skip is set. +- * Will roll back to continue process missed tds. ++ * Do not update event ring dequeue pointer if we're in a loop ++ * processing missed tds. + */ +- if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { ++ if (!handling_skipped_tds) + inc_deq(xhci, xhci->event_ring); +- } + + if (ret) { + urb = td->urb; +@@ -2662,7 +2672,7 @@ cleanup: + * Process them as short transfer until reach the td pointed by + * the event. + */ +- } while (ep->skip && trb_comp_code != COMP_MISSED_INT); ++ } while (handling_skipped_tds); + + return 0; + } +diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h +index 898be3a8db9a..6d8f8fba3341 100644 +--- a/include/sound/wm8904.h ++++ b/include/sound/wm8904.h +@@ -119,7 +119,7 @@ + #define WM8904_MIC_REGS 2 + #define WM8904_GPIO_REGS 4 + #define WM8904_DRC_REGS 4 +-#define WM8904_EQ_REGS 25 ++#define WM8904_EQ_REGS 24 + + /** + * DRC configurations are specified with a label and a set of register +diff --git a/kernel/module.c b/kernel/module.c +index 61fb677211cb..fd2afdf48a89 100644 +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -942,11 +942,15 @@ void symbol_put_addr(void *addr) + if (core_kernel_text(a)) + return; + +- /* module_text_address is safe here: we're supposed to have reference +- * to module from symbol_get, so it can't go away. */ ++ /* ++ * Even though we hold a reference on the module; we still need to ++ * disable preemption in order to safely traverse the data structure. ++ */ ++ preempt_disable(); + modaddr = __module_text_address(a); + BUG_ON(!modaddr); + module_put(modaddr); ++ preempt_enable(); + } + EXPORT_SYMBOL_GPL(symbol_put_addr); + +diff --git a/mm/filemap.c b/mm/filemap.c +index 7905fe721aa8..725a10043244 100644 +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2340,6 +2340,11 @@ again: + break; + } + ++ if (fatal_signal_pending(current)) { ++ status = -EINTR; ++ break; ++ } ++ + status = a_ops->write_begin(file, mapping, pos, bytes, flags, + &page, &fsdata); + if (unlikely(status)) +@@ -2380,10 +2385,6 @@ again: + written += copied; + + balance_dirty_pages_ratelimited(mapping); +- if (fatal_signal_pending(current)) { +- status = -EINTR; +- break; +- } + } while (iov_iter_count(i)); + + return written ? written : status; diff --git a/patch/kernel/odroid-next/packaging-next.patch b/patch/kernel/odroid-next/packaging-next.patch new file mode 100644 index 000000000..cf42d6ee2 --- /dev/null +++ b/patch/kernel/odroid-next/packaging-next.patch @@ -0,0 +1,152 @@ +diff --git a/scripts/package/builddeb b/scripts/package/builddeb +index 88dbf23..fc023a9 100755 +--- a/scripts/package/builddeb ++++ b/scripts/package/builddeb +@@ -80,11 +80,13 @@ tmpdir="$objtree/debian/tmp" + fwdir="$objtree/debian/fwtmp" + kernel_headers_dir="$objtree/debian/hdrtmp" + libc_headers_dir="$objtree/debian/headertmp" ++dtb_dir="$objtree/debian/dtbtmp" + 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-next"$LOCALVERSION" ++fwpackagename=linux-firmware-image-next"$LOCALVERSION" ++kernel_headers_packagename=linux-headers-next"$LOCALVERSION" ++dtb_packagename=linux-dtb-next"$LOCALVERSION" ++libc_headers_packagename=linux-libc-dev-next"$LOCALVERSION" + dbg_packagename=$packagename-dbg + + if [ "$ARCH" = "um" ] ; then +@@ -108,13 +110,17 @@ esac + BUILD_DEBUG="$(grep -s '^CONFIG_DEBUG_INFO=y' $KCONFIG_CONFIG || true)" + + # Setup the directory structure +-rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" ++rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" "$dtb_dir" + mkdir -m 755 -p "$tmpdir/DEBIAN" + mkdir -p "$tmpdir/lib" "$tmpdir/boot" "$tmpdir/usr/share/doc/$packagename" + mkdir -m 755 -p "$fwdir/DEBIAN" + mkdir -p "$fwdir/lib/firmware/$version/" "$fwdir/usr/share/doc/$fwpackagename" + mkdir -m 755 -p "$libc_headers_dir/DEBIAN" + mkdir -p "$libc_headers_dir/usr/share/doc/$libc_headers_packagename" ++ ++mkdir -m 755 -p "$dtb_dir/DEBIAN" ++mkdir -p "$dtb_dir/boot/dtb" "$dtb_dir/usr/share/doc/$dtb_packagename" ++ + mkdir -m 755 -p "$kernel_headers_dir/DEBIAN" + mkdir -p "$kernel_headers_dir/usr/share/doc/$kernel_headers_packagename" + mkdir -p "$kernel_headers_dir/lib/modules/$version/" +@@ -165,6 +171,11 @@ if grep -q '^CONFIG_MODULES=y' $KCONFIG_CONFIG ; then + fi + fi + ++if grep -q '^CONFIG_OF=y' $KCONFIG_CONFIG ; then ++ #mkdir -p "$tmpdir/boot/dtb" ++ INSTALL_DTBS_PATH="$dtb_dir/boot/dtb" $MAKE KBUILD_SRC= dtbs_install ++fi ++ + if [ "$ARCH" != "um" ]; then + $MAKE headers_check KBUILD_SRC= + $MAKE headers_install KBUILD_SRC= INSTALL_HDR_PATH="$libc_headers_dir/usr" +@@ -177,7 +188,7 @@ fi + # so do we; recent versions of dracut and initramfs-tools will obey this. + debhookdir=${KDEB_HOOKDIR:-/etc/kernel} + if grep -q '^CONFIG_BLK_DEV_INITRD=y' $KCONFIG_CONFIG; then +- want_initrd=Yes ++ want_initrd=Yes + else + want_initrd=No + fi +@@ -189,9 +200,11 @@ for script in postinst postrm preinst prerm ; do + set -e + + # Pass maintainer script parameters to hook scripts ++ + export DEB_MAINT_PARAMS="\$*" + + # Tell initramfs builder whether it's wanted ++ + export INITRD=$want_initrd + + test -d $debhookdir/$script.d && run-parts --arg="$version" --arg="/$installed_image_path" $debhookdir/$script.d +@@ -200,6 +213,29 @@ EOF + chmod 755 "$tmpdir/DEBIAN/$script" + done + ++## ++## Create sym link to kernel image ++## ++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 ++touch /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 + email=$DEBEMAIL +@@ -306,6 +342,12 @@ fi + (cd $objtree; find arch/$SRCARCH/include Module.symvers include scripts -type f) >> "$objtree/debian/hdrobjfiles" + destdir=$kernel_headers_dir/usr/src/linux-headers-$version + mkdir -p "$destdir" ++######################## headers patch ++ZACNI=$(pwd) ++cd $destdir ++patch -p1 < /tmp/headers-debian-byteshift.patch ++cd $ZACNI ++######################## headers patch + (cd $srctree; tar -c -f - -T -) < "$objtree/debian/hdrsrcfiles" | (cd $destdir; tar -xf -) + (cd $objtree; tar -c -f - -T -) < "$objtree/debian/hdrobjfiles" | (cd $destdir; tar -xf -) + (cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be +@@ -315,7 +357,7 @@ rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles" + cat <> debian/control + + Package: $kernel_headers_packagename +-Provides: linux-headers, linux-headers-2.6 ++Provides: linux-headers + Architecture: any + Description: Linux kernel headers for $KERNELRELEASE on \${kernel:debarch} + This package provides kernel header files for $KERNELRELEASE on \${kernel:debarch} +@@ -341,6 +383,16 @@ fi + + cat <> debian/control + ++Package: $dtb_packagename ++Architecture: any ++Description: Linux DTB, version $version ++ This package contains device blobs from the Linux kernel, version $version. ++EOF ++ ++create_package "$dtb_packagename" "$dtb_dir" ++ ++cat <> debian/control ++ + Package: $libc_headers_packagename + Section: devel + Provides: linux-kernel-headers +@@ -352,7 +404,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/odroid-next/patch-4.2.0-1.patch b/patch/kernel/odroid-next/patch-4.2.0-1.patch new file mode 100644 index 000000000..c2b604c24 --- /dev/null +++ b/patch/kernel/odroid-next/patch-4.2.0-1.patch @@ -0,0 +1,4509 @@ +diff --git a/Documentation/ABI/testing/configfs-usb-gadget-loopback b/Documentation/ABI/testing/configfs-usb-gadget-loopback +index 9aae5bfb9908..06beefbcf061 100644 +--- a/Documentation/ABI/testing/configfs-usb-gadget-loopback ++++ b/Documentation/ABI/testing/configfs-usb-gadget-loopback +@@ -5,4 +5,4 @@ Description: + The attributes: + + qlen - depth of loopback queue +- bulk_buflen - buffer length ++ buflen - buffer length +diff --git a/Documentation/ABI/testing/configfs-usb-gadget-sourcesink b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink +index 29477c319f61..bc7ff731aa0c 100644 +--- a/Documentation/ABI/testing/configfs-usb-gadget-sourcesink ++++ b/Documentation/ABI/testing/configfs-usb-gadget-sourcesink +@@ -9,4 +9,4 @@ Description: + isoc_maxpacket - 0 - 1023 (fs), 0 - 1024 (hs/ss) + isoc_mult - 0..2 (hs/ss only) + isoc_maxburst - 0..15 (ss only) +- qlen - buffer length ++ buflen - buffer length +diff --git a/Documentation/device-mapper/statistics.txt b/Documentation/device-mapper/statistics.txt +index 4919b2dfd1b3..6f5ef944ca4c 100644 +--- a/Documentation/device-mapper/statistics.txt ++++ b/Documentation/device-mapper/statistics.txt +@@ -121,6 +121,10 @@ Messages + + Output format: + : + ++ precise_timestamps histogram:n1,n2,n3,... ++ ++ The strings "precise_timestamps" and "histogram" are printed only ++ if they were specified when creating the region. + + @stats_print [ ] + +diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt +index 592678009c15..b24d3ef89166 100644 +--- a/Documentation/usb/gadget-testing.txt ++++ b/Documentation/usb/gadget-testing.txt +@@ -237,9 +237,7 @@ Testing the LOOPBACK function + ----------------------------- + + device: run the gadget +-host: test-usb +- +-http://www.linux-usb.org/usbtest/testusb.c ++host: test-usb (tools/usb/testusb.c) + + 8. MASS STORAGE function + ======================== +@@ -586,9 +584,8 @@ Testing the SOURCESINK function + ------------------------------- + + device: run the gadget +-host: test-usb ++host: test-usb (tools/usb/testusb.c) + +-http://www.linux-usb.org/usbtest/testusb.c + + 16. UAC1 function + ================= +diff --git a/Makefile b/Makefile +index c3615937df38..a03efc18aa48 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 2 +-SUBLEVEL = 0 ++SUBLEVEL = 1 + EXTRAVERSION = + NAME = Hurr durr I'ma sheep + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 1c5021002fe4..ede2526ecf1f 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -536,6 +536,7 @@ config ARCH_ORION5X + select MVEBU_MBUS + select PCI + select PLAT_ORION_LEGACY ++ select MULTI_IRQ_HANDLER + help + Support for the following Marvell Orion 5x series SoCs: + Orion-1 (5181), Orion-VoIP (5181L), Orion-NAS (5182), +diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi +index 22316d00493e..858efd0c861d 100644 +--- a/arch/arm/boot/dts/rk3288.dtsi ++++ b/arch/arm/boot/dts/rk3288.dtsi +@@ -626,7 +626,7 @@ + compatible = "rockchip,rk3288-wdt", "snps,dw-wdt"; + reg = <0xff800000 0x100>; + clocks = <&cru PCLK_WDT>; +- interrupts = ; ++ interrupts = ; + status = "disabled"; + }; + +diff --git a/arch/arm/mach-bcm/bcm63xx_smp.c b/arch/arm/mach-bcm/bcm63xx_smp.c +index 3f014f18cea5..b8e18cc8f237 100644 +--- a/arch/arm/mach-bcm/bcm63xx_smp.c ++++ b/arch/arm/mach-bcm/bcm63xx_smp.c +@@ -127,7 +127,7 @@ static int bcm63138_smp_boot_secondary(unsigned int cpu, + } + + /* Locate the secondary CPU node */ +- dn = of_get_cpu_node(cpu_logical_map(cpu), NULL); ++ dn = of_get_cpu_node(cpu, NULL); + if (!dn) { + pr_err("SMP: failed to locate secondary CPU%d node\n", cpu); + ret = -ENODEV; +diff --git a/arch/arm/mach-omap2/clockdomains7xx_data.c b/arch/arm/mach-omap2/clockdomains7xx_data.c +index 57d5df0c1fbd..7581e036bda6 100644 +--- a/arch/arm/mach-omap2/clockdomains7xx_data.c ++++ b/arch/arm/mach-omap2/clockdomains7xx_data.c +@@ -331,7 +331,7 @@ static struct clockdomain l4per2_7xx_clkdm = { + .dep_bit = DRA7XX_L4PER2_STATDEP_SHIFT, + .wkdep_srcs = l4per2_wkup_sleep_deps, + .sleepdep_srcs = l4per2_wkup_sleep_deps, +- .flags = CLKDM_CAN_HWSUP_SWSUP, ++ .flags = CLKDM_CAN_SWSUP, + }; + + static struct clockdomain mpu0_7xx_clkdm = { +diff --git a/arch/arm/mach-orion5x/include/mach/irqs.h b/arch/arm/mach-orion5x/include/mach/irqs.h +index a6fa9d8f12d8..2431d9923427 100644 +--- a/arch/arm/mach-orion5x/include/mach/irqs.h ++++ b/arch/arm/mach-orion5x/include/mach/irqs.h +@@ -16,42 +16,42 @@ + /* + * Orion Main Interrupt Controller + */ +-#define IRQ_ORION5X_BRIDGE 0 +-#define IRQ_ORION5X_DOORBELL_H2C 1 +-#define IRQ_ORION5X_DOORBELL_C2H 2 +-#define IRQ_ORION5X_UART0 3 +-#define IRQ_ORION5X_UART1 4 +-#define IRQ_ORION5X_I2C 5 +-#define IRQ_ORION5X_GPIO_0_7 6 +-#define IRQ_ORION5X_GPIO_8_15 7 +-#define IRQ_ORION5X_GPIO_16_23 8 +-#define IRQ_ORION5X_GPIO_24_31 9 +-#define IRQ_ORION5X_PCIE0_ERR 10 +-#define IRQ_ORION5X_PCIE0_INT 11 +-#define IRQ_ORION5X_USB1_CTRL 12 +-#define IRQ_ORION5X_DEV_BUS_ERR 14 +-#define IRQ_ORION5X_PCI_ERR 15 +-#define IRQ_ORION5X_USB_BR_ERR 16 +-#define IRQ_ORION5X_USB0_CTRL 17 +-#define IRQ_ORION5X_ETH_RX 18 +-#define IRQ_ORION5X_ETH_TX 19 +-#define IRQ_ORION5X_ETH_MISC 20 +-#define IRQ_ORION5X_ETH_SUM 21 +-#define IRQ_ORION5X_ETH_ERR 22 +-#define IRQ_ORION5X_IDMA_ERR 23 +-#define IRQ_ORION5X_IDMA_0 24 +-#define IRQ_ORION5X_IDMA_1 25 +-#define IRQ_ORION5X_IDMA_2 26 +-#define IRQ_ORION5X_IDMA_3 27 +-#define IRQ_ORION5X_CESA 28 +-#define IRQ_ORION5X_SATA 29 +-#define IRQ_ORION5X_XOR0 30 +-#define IRQ_ORION5X_XOR1 31 ++#define IRQ_ORION5X_BRIDGE (1 + 0) ++#define IRQ_ORION5X_DOORBELL_H2C (1 + 1) ++#define IRQ_ORION5X_DOORBELL_C2H (1 + 2) ++#define IRQ_ORION5X_UART0 (1 + 3) ++#define IRQ_ORION5X_UART1 (1 + 4) ++#define IRQ_ORION5X_I2C (1 + 5) ++#define IRQ_ORION5X_GPIO_0_7 (1 + 6) ++#define IRQ_ORION5X_GPIO_8_15 (1 + 7) ++#define IRQ_ORION5X_GPIO_16_23 (1 + 8) ++#define IRQ_ORION5X_GPIO_24_31 (1 + 9) ++#define IRQ_ORION5X_PCIE0_ERR (1 + 10) ++#define IRQ_ORION5X_PCIE0_INT (1 + 11) ++#define IRQ_ORION5X_USB1_CTRL (1 + 12) ++#define IRQ_ORION5X_DEV_BUS_ERR (1 + 14) ++#define IRQ_ORION5X_PCI_ERR (1 + 15) ++#define IRQ_ORION5X_USB_BR_ERR (1 + 16) ++#define IRQ_ORION5X_USB0_CTRL (1 + 17) ++#define IRQ_ORION5X_ETH_RX (1 + 18) ++#define IRQ_ORION5X_ETH_TX (1 + 19) ++#define IRQ_ORION5X_ETH_MISC (1 + 20) ++#define IRQ_ORION5X_ETH_SUM (1 + 21) ++#define IRQ_ORION5X_ETH_ERR (1 + 22) ++#define IRQ_ORION5X_IDMA_ERR (1 + 23) ++#define IRQ_ORION5X_IDMA_0 (1 + 24) ++#define IRQ_ORION5X_IDMA_1 (1 + 25) ++#define IRQ_ORION5X_IDMA_2 (1 + 26) ++#define IRQ_ORION5X_IDMA_3 (1 + 27) ++#define IRQ_ORION5X_CESA (1 + 28) ++#define IRQ_ORION5X_SATA (1 + 29) ++#define IRQ_ORION5X_XOR0 (1 + 30) ++#define IRQ_ORION5X_XOR1 (1 + 31) + + /* + * Orion General Purpose Pins + */ +-#define IRQ_ORION5X_GPIO_START 32 ++#define IRQ_ORION5X_GPIO_START 33 + #define NR_GPIO_IRQS 32 + + #define NR_IRQS (IRQ_ORION5X_GPIO_START + NR_GPIO_IRQS) +diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c +index cd4bac4d7e43..086ecb87d885 100644 +--- a/arch/arm/mach-orion5x/irq.c ++++ b/arch/arm/mach-orion5x/irq.c +@@ -42,7 +42,7 @@ __exception_irq_entry orion5x_legacy_handle_irq(struct pt_regs *regs) + stat = readl_relaxed(MAIN_IRQ_CAUSE); + stat &= readl_relaxed(MAIN_IRQ_MASK); + if (stat) { +- unsigned int hwirq = __fls(stat); ++ unsigned int hwirq = 1 + __fls(stat); + handle_IRQ(hwirq, regs); + return; + } +@@ -51,7 +51,7 @@ __exception_irq_entry orion5x_legacy_handle_irq(struct pt_regs *regs) + + void __init orion5x_init_irq(void) + { +- orion_irq_init(0, MAIN_IRQ_MASK); ++ orion_irq_init(1, MAIN_IRQ_MASK); + + #ifdef CONFIG_MULTI_IRQ_HANDLER + set_handle_irq(orion5x_legacy_handle_irq); +diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c +index 8fcec1cc101e..01b3e3683ede 100644 +--- a/arch/arm/mach-rockchip/platsmp.c ++++ b/arch/arm/mach-rockchip/platsmp.c +@@ -72,29 +72,22 @@ static struct reset_control *rockchip_get_core_reset(int cpu) + static int pmu_set_power_domain(int pd, bool on) + { + u32 val = (on) ? 0 : BIT(pd); ++ struct reset_control *rstc = rockchip_get_core_reset(pd); + int ret; + ++ if (IS_ERR(rstc) && read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) { ++ pr_err("%s: could not get reset control for core %d\n", ++ __func__, pd); ++ return PTR_ERR(rstc); ++ } ++ + /* + * We need to soft reset the cpu when we turn off the cpu power domain, + * or else the active processors might be stalled when the individual + * processor is powered down. + */ +- if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) { +- struct reset_control *rstc = rockchip_get_core_reset(pd); +- +- if (IS_ERR(rstc)) { +- pr_err("%s: could not get reset control for core %d\n", +- __func__, pd); +- return PTR_ERR(rstc); +- } +- +- if (on) +- reset_control_deassert(rstc); +- else +- reset_control_assert(rstc); +- +- reset_control_put(rstc); +- } ++ if (!IS_ERR(rstc) && !on) ++ reset_control_assert(rstc); + + ret = regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), val); + if (ret < 0) { +@@ -112,6 +105,12 @@ static int pmu_set_power_domain(int pd, bool on) + } + } + ++ if (!IS_ERR(rstc)) { ++ if (on) ++ reset_control_deassert(rstc); ++ reset_control_put(rstc); ++ } ++ + return 0; + } + +@@ -146,8 +145,12 @@ static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle) + * the mailbox: + * sram_base_addr + 4: 0xdeadbeaf + * sram_base_addr + 8: start address for pc ++ * The cpu0 need to wait the other cpus other than cpu0 entering ++ * the wfe state.The wait time is affected by many aspects. ++ * (e.g: cpu frequency, bootrom frequency, sram frequency, ...) + * */ +- udelay(10); ++ mdelay(1); /* ensure the cpus other than cpu0 to startup */ ++ + writel(virt_to_phys(secondary_startup), sram_base_addr + 8); + writel(0xDEADBEAF, sram_base_addr + 4); + dsb_sev(); +diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c +index b027a89737b6..c6d601cc9764 100644 +--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c ++++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c +@@ -421,14 +421,20 @@ long kvmppc_do_h_remove(struct kvm *kvm, unsigned long flags, + rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]); + v = pte & ~HPTE_V_HVLOCK; + if (v & HPTE_V_VALID) { +- u64 pte1; +- +- pte1 = be64_to_cpu(hpte[1]); + hpte[0] &= ~cpu_to_be64(HPTE_V_VALID); +- rb = compute_tlbie_rb(v, pte1, pte_index); ++ rb = compute_tlbie_rb(v, be64_to_cpu(hpte[1]), pte_index); + do_tlbies(kvm, &rb, 1, global_invalidates(kvm, flags), true); +- /* Read PTE low word after tlbie to get final R/C values */ +- remove_revmap_chain(kvm, pte_index, rev, v, pte1); ++ /* ++ * The reference (R) and change (C) bits in a HPT ++ * entry can be set by hardware at any time up until ++ * the HPTE is invalidated and the TLB invalidation ++ * sequence has completed. This means that when ++ * removing a HPTE, we need to re-read the HPTE after ++ * the invalidation sequence has completed in order to ++ * obtain reliable values of R and C. ++ */ ++ remove_revmap_chain(kvm, pte_index, rev, v, ++ be64_to_cpu(hpte[1])); + } + r = rev->guest_rpte & ~HPTE_GR_RESERVED; + note_hpte_modification(kvm, rev); +diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S +index faa86e9c0551..76408cf0ad04 100644 +--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S ++++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S +@@ -1127,6 +1127,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR) + cmpwi r12, BOOK3S_INTERRUPT_H_DOORBELL + bne 3f + lbz r0, HSTATE_HOST_IPI(r13) ++ cmpwi r0, 0 + beq 4f + b guest_exit_cont + 3: +diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c +index ca070d260af2..b80512b9ef59 100644 +--- a/arch/s390/kernel/setup.c ++++ b/arch/s390/kernel/setup.c +@@ -688,7 +688,7 @@ static void __init setup_memory(void) + /* + * Setup hardware capabilities. + */ +-static void __init setup_hwcaps(void) ++static int __init setup_hwcaps(void) + { + static const int stfl_bits[6] = { 0, 2, 7, 17, 19, 21 }; + struct cpuid cpu_id; +@@ -754,9 +754,11 @@ static void __init setup_hwcaps(void) + elf_hwcap |= HWCAP_S390_TE; + + /* +- * Vector extension HWCAP_S390_VXRS is bit 11. ++ * Vector extension HWCAP_S390_VXRS is bit 11. The Vector extension ++ * can be disabled with the "novx" parameter. Use MACHINE_HAS_VX ++ * instead of facility bit 129. + */ +- if (test_facility(129)) ++ if (MACHINE_HAS_VX) + elf_hwcap |= HWCAP_S390_VXRS; + get_cpu_id(&cpu_id); + add_device_randomness(&cpu_id, sizeof(cpu_id)); +@@ -793,7 +795,9 @@ static void __init setup_hwcaps(void) + strcpy(elf_platform, "z13"); + break; + } ++ return 0; + } ++arch_initcall(setup_hwcaps); + + /* + * Add system information as device randomness +@@ -881,11 +885,6 @@ void __init setup_arch(char **cmdline_p) + cpu_init(); + + /* +- * Setup capabilities (ELF_HWCAP & ELF_PLATFORM). +- */ +- setup_hwcaps(); +- +- /* + * Create kernel page tables and switch to virtual addressing. + */ + paging_init(); +diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c +index 64d7cf1b50e1..440df0c7a2ee 100644 +--- a/arch/x86/crypto/ghash-clmulni-intel_glue.c ++++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c +@@ -294,6 +294,7 @@ static struct ahash_alg ghash_async_alg = { + .cra_name = "ghash", + .cra_driver_name = "ghash-clmulni", + .cra_priority = 400, ++ .cra_ctxsize = sizeof(struct ghash_async_ctx), + .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, + .cra_blocksize = GHASH_BLOCK_SIZE, + .cra_type = &crypto_ahash_type, +diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c +index e49ee24da85e..9393896717d0 100644 +--- a/arch/x86/kernel/acpi/boot.c ++++ b/arch/x86/kernel/acpi/boot.c +@@ -445,6 +445,7 @@ static void __init acpi_sci_ioapic_setup(u8 bus_irq, u16 polarity, u16 trigger, + polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK; + + mp_override_legacy_irq(bus_irq, polarity, trigger, gsi); ++ acpi_penalize_sci_irq(bus_irq, trigger, polarity); + + /* + * stash over-ride to indicate we've been here +diff --git a/arch/x86/kernel/cpu/mcheck/mce_intel.c b/arch/x86/kernel/cpu/mcheck/mce_intel.c +index 844f56c5616d..c93c27df9919 100644 +--- a/arch/x86/kernel/cpu/mcheck/mce_intel.c ++++ b/arch/x86/kernel/cpu/mcheck/mce_intel.c +@@ -146,6 +146,27 @@ void mce_intel_hcpu_update(unsigned long cpu) + per_cpu(cmci_storm_state, cpu) = CMCI_STORM_NONE; + } + ++static void cmci_toggle_interrupt_mode(bool on) ++{ ++ unsigned long flags, *owned; ++ int bank; ++ u64 val; ++ ++ raw_spin_lock_irqsave(&cmci_discover_lock, flags); ++ owned = this_cpu_ptr(mce_banks_owned); ++ for_each_set_bit(bank, owned, MAX_NR_BANKS) { ++ rdmsrl(MSR_IA32_MCx_CTL2(bank), val); ++ ++ if (on) ++ val |= MCI_CTL2_CMCI_EN; ++ else ++ val &= ~MCI_CTL2_CMCI_EN; ++ ++ wrmsrl(MSR_IA32_MCx_CTL2(bank), val); ++ } ++ raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); ++} ++ + unsigned long cmci_intel_adjust_timer(unsigned long interval) + { + if ((this_cpu_read(cmci_backoff_cnt) > 0) && +@@ -175,7 +196,7 @@ unsigned long cmci_intel_adjust_timer(unsigned long interval) + */ + if (!atomic_read(&cmci_storm_on_cpus)) { + __this_cpu_write(cmci_storm_state, CMCI_STORM_NONE); +- cmci_reenable(); ++ cmci_toggle_interrupt_mode(true); + cmci_recheck(); + } + return CMCI_POLL_INTERVAL; +@@ -186,22 +207,6 @@ unsigned long cmci_intel_adjust_timer(unsigned long interval) + } + } + +-static void cmci_storm_disable_banks(void) +-{ +- unsigned long flags, *owned; +- int bank; +- u64 val; +- +- raw_spin_lock_irqsave(&cmci_discover_lock, flags); +- owned = this_cpu_ptr(mce_banks_owned); +- for_each_set_bit(bank, owned, MAX_NR_BANKS) { +- rdmsrl(MSR_IA32_MCx_CTL2(bank), val); +- val &= ~MCI_CTL2_CMCI_EN; +- wrmsrl(MSR_IA32_MCx_CTL2(bank), val); +- } +- raw_spin_unlock_irqrestore(&cmci_discover_lock, flags); +-} +- + static bool cmci_storm_detect(void) + { + unsigned int cnt = __this_cpu_read(cmci_storm_cnt); +@@ -223,7 +228,7 @@ static bool cmci_storm_detect(void) + if (cnt <= CMCI_STORM_THRESHOLD) + return false; + +- cmci_storm_disable_banks(); ++ cmci_toggle_interrupt_mode(false); + __this_cpu_write(cmci_storm_state, CMCI_STORM_ACTIVE); + r = atomic_add_return(1, &cmci_storm_on_cpus); + mce_timer_kick(CMCI_STORM_INTERVAL); +diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c +index 44171462bd2a..82362ad2f25d 100644 +--- a/arch/x86/kvm/mmu.c ++++ b/arch/x86/kvm/mmu.c +@@ -357,12 +357,6 @@ static u64 __get_spte_lockless(u64 *sptep) + { + return ACCESS_ONCE(*sptep); + } +- +-static bool __check_direct_spte_mmio_pf(u64 spte) +-{ +- /* It is valid if the spte is zapped. */ +- return spte == 0ull; +-} + #else + union split_spte { + struct { +@@ -478,23 +472,6 @@ retry: + + return spte.spte; + } +- +-static bool __check_direct_spte_mmio_pf(u64 spte) +-{ +- union split_spte sspte = (union split_spte)spte; +- u32 high_mmio_mask = shadow_mmio_mask >> 32; +- +- /* It is valid if the spte is zapped. */ +- if (spte == 0ull) +- return true; +- +- /* It is valid if the spte is being zapped. */ +- if (sspte.spte_low == 0ull && +- (sspte.spte_high & high_mmio_mask) == high_mmio_mask) +- return true; +- +- return false; +-} + #endif + + static bool spte_is_locklessly_modifiable(u64 spte) +@@ -3299,21 +3276,6 @@ static bool quickly_check_mmio_pf(struct kvm_vcpu *vcpu, u64 addr, bool direct) + return vcpu_match_mmio_gva(vcpu, addr); + } + +- +-/* +- * On direct hosts, the last spte is only allows two states +- * for mmio page fault: +- * - It is the mmio spte +- * - It is zapped or it is being zapped. +- * +- * This function completely checks the spte when the last spte +- * is not the mmio spte. +- */ +-static bool check_direct_spte_mmio_pf(u64 spte) +-{ +- return __check_direct_spte_mmio_pf(spte); +-} +- + static u64 walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr) + { + struct kvm_shadow_walk_iterator iterator; +@@ -3356,13 +3318,6 @@ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct) + } + + /* +- * It's ok if the gva is remapped by other cpus on shadow guest, +- * it's a BUG if the gfn is not a mmio page. +- */ +- if (direct && !check_direct_spte_mmio_pf(spte)) +- return RET_MMIO_PF_BUG; +- +- /* + * If the page table is zapped by other cpus, let CPU fault again on + * the address. + */ +diff --git a/arch/xtensa/include/asm/traps.h b/arch/xtensa/include/asm/traps.h +index 677bfcf4ee5d..28f33a8b7f5f 100644 +--- a/arch/xtensa/include/asm/traps.h ++++ b/arch/xtensa/include/asm/traps.h +@@ -25,30 +25,39 @@ static inline void spill_registers(void) + { + #if XCHAL_NUM_AREGS > 16 + __asm__ __volatile__ ( +- " call12 1f\n" ++ " call8 1f\n" + " _j 2f\n" + " retw\n" + " .align 4\n" + "1:\n" ++#if XCHAL_NUM_AREGS == 32 ++ " _entry a1, 32\n" ++ " addi a8, a0, 3\n" ++ " _entry a1, 16\n" ++ " mov a12, a12\n" ++ " retw\n" ++#else + " _entry a1, 48\n" +- " addi a12, a0, 3\n" +-#if XCHAL_NUM_AREGS > 32 +- " .rept (" __stringify(XCHAL_NUM_AREGS) " - 32) / 12\n" ++ " call12 1f\n" ++ " retw\n" ++ " .align 4\n" ++ "1:\n" ++ " .rept (" __stringify(XCHAL_NUM_AREGS) " - 16) / 12\n" + " _entry a1, 48\n" + " mov a12, a0\n" + " .endr\n" +-#endif +- " _entry a1, 48\n" ++ " _entry a1, 16\n" + #if XCHAL_NUM_AREGS % 12 == 0 +- " mov a8, a8\n" +-#elif XCHAL_NUM_AREGS % 12 == 4 + " mov a12, a12\n" +-#elif XCHAL_NUM_AREGS % 12 == 8 ++#elif XCHAL_NUM_AREGS % 12 == 4 + " mov a4, a4\n" ++#elif XCHAL_NUM_AREGS % 12 == 8 ++ " mov a8, a8\n" + #endif + " retw\n" ++#endif + "2:\n" +- : : : "a12", "a13", "memory"); ++ : : : "a8", "a9", "memory"); + #else + __asm__ __volatile__ ( + " mov a12, a12\n" +diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S +index 82bbfa5a05b3..a2a902140c4e 100644 +--- a/arch/xtensa/kernel/entry.S ++++ b/arch/xtensa/kernel/entry.S +@@ -568,12 +568,13 @@ user_exception_exit: + * (if we have restored WSBITS-1 frames). + */ + ++2: + #if XCHAL_HAVE_THREADPTR + l32i a3, a1, PT_THREADPTR + wur a3, threadptr + #endif + +-2: j common_exception_exit ++ j common_exception_exit + + /* This is the kernel exception exit. + * We avoided to do a MOVSP when we entered the exception, but we +@@ -1820,7 +1821,7 @@ ENDPROC(system_call) + mov a12, a0 + .endr + #endif +- _entry a1, 48 ++ _entry a1, 16 + #if XCHAL_NUM_AREGS % 12 == 0 + mov a8, a8 + #elif XCHAL_NUM_AREGS % 12 == 4 +@@ -1844,7 +1845,7 @@ ENDPROC(system_call) + + ENTRY(_switch_to) + +- entry a1, 16 ++ entry a1, 48 + + mov a11, a3 # and 'next' (a3) + +diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c +index ff6d8adc9cda..fb765524cc3d 100644 +--- a/drivers/acpi/acpi_pnp.c ++++ b/drivers/acpi/acpi_pnp.c +@@ -153,6 +153,7 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { + {"AEI0250"}, /* PROLiNK 1456VH ISA PnP K56flex Fax Modem */ + {"AEI1240"}, /* Actiontec ISA PNP 56K X2 Fax Modem */ + {"AKY1021"}, /* Rockwell 56K ACF II Fax+Data+Voice Modem */ ++ {"ALI5123"}, /* ALi Fast Infrared Controller */ + {"AZT4001"}, /* AZT3005 PnP SOUND DEVICE */ + {"BDP3336"}, /* Best Data Products Inc. Smart One 336F PnP Modem */ + {"BRI0A49"}, /* Boca Complete Ofc Communicator 14.4 Data-FAX */ +diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c +index cfd7581cc19f..b09ad554430a 100644 +--- a/drivers/acpi/pci_link.c ++++ b/drivers/acpi/pci_link.c +@@ -826,6 +826,22 @@ void acpi_penalize_isa_irq(int irq, int active) + } + + /* ++ * Penalize IRQ used by ACPI SCI. If ACPI SCI pin attributes conflict with ++ * PCI IRQ attributes, mark ACPI SCI as ISA_ALWAYS so it won't be use for ++ * PCI IRQs. ++ */ ++void acpi_penalize_sci_irq(int irq, int trigger, int polarity) ++{ ++ if (irq >= 0 && irq < ARRAY_SIZE(acpi_irq_penalty)) { ++ if (trigger != ACPI_MADT_TRIGGER_LEVEL || ++ polarity != ACPI_MADT_POLARITY_ACTIVE_LOW) ++ acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_ALWAYS; ++ else ++ acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING; ++ } ++} ++ ++/* + * Over-ride default table to reserve additional IRQs for use by ISA + * e.g. acpi_irq_isa=5 + * Useful for telling ACPI how not to interfere with your ISA sound card. +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index 7e62751abfac..a46660204e3a 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -351,6 +351,7 @@ static const struct pci_device_id ahci_pci_tbl[] = { + /* JMicron 362B and 362C have an AHCI function with IDE class code */ + { PCI_VDEVICE(JMICRON, 0x2362), board_ahci_ign_iferr }, + { PCI_VDEVICE(JMICRON, 0x236f), board_ahci_ign_iferr }, ++ /* May need to update quirk_jmicron_async_suspend() for additions */ + + /* ATI */ + { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ +@@ -1451,18 +1452,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) + else if (pdev->vendor == 0x177d && pdev->device == 0xa01c) + ahci_pci_bar = AHCI_PCI_BAR_CAVIUM; + +- /* +- * The JMicron chip 361/363 contains one SATA controller and one +- * PATA controller,for powering on these both controllers, we must +- * follow the sequence one by one, otherwise one of them can not be +- * powered on successfully, so here we disable the async suspend +- * method for these chips. +- */ +- if (pdev->vendor == PCI_VENDOR_ID_JMICRON && +- (pdev->device == PCI_DEVICE_ID_JMICRON_JMB363 || +- pdev->device == PCI_DEVICE_ID_JMICRON_JMB361)) +- device_disable_async_suspend(&pdev->dev); +- + /* acquire resources */ + rc = pcim_enable_device(pdev); + if (rc) +diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c +index 47e418b8c8ba..4d1a5d2c4287 100644 +--- a/drivers/ata/pata_jmicron.c ++++ b/drivers/ata/pata_jmicron.c +@@ -143,18 +143,6 @@ static int jmicron_init_one (struct pci_dev *pdev, const struct pci_device_id *i + }; + const struct ata_port_info *ppi[] = { &info, NULL }; + +- /* +- * The JMicron chip 361/363 contains one SATA controller and one +- * PATA controller,for powering on these both controllers, we must +- * follow the sequence one by one, otherwise one of them can not be +- * powered on successfully, so here we disable the async suspend +- * method for these chips. +- */ +- if (pdev->vendor == PCI_VENDOR_ID_JMICRON && +- (pdev->device == PCI_DEVICE_ID_JMICRON_JMB363 || +- pdev->device == PCI_DEVICE_ID_JMICRON_JMB361)) +- device_disable_async_suspend(&pdev->dev); +- + return ata_pci_bmdma_init_one(pdev, ppi, &jmicron_sht, NULL, 0); + } + +diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c +index 5b93852392b8..0d752851a1ee 100644 +--- a/drivers/auxdisplay/ks0108.c ++++ b/drivers/auxdisplay/ks0108.c +@@ -139,6 +139,7 @@ static int __init ks0108_init(void) + + ks0108_pardevice = parport_register_device(ks0108_parport, KS0108_NAME, + NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); ++ parport_put_port(ks0108_parport); + if (ks0108_pardevice == NULL) { + printk(KERN_ERR KS0108_NAME ": ERROR: " + "parport didn't register new device\n"); +diff --git a/drivers/base/devres.c b/drivers/base/devres.c +index c8a53d1e019f..875464690117 100644 +--- a/drivers/base/devres.c ++++ b/drivers/base/devres.c +@@ -297,10 +297,10 @@ void * devres_get(struct device *dev, void *new_res, + if (!dr) { + add_dr(dev, &new_dr->node); + dr = new_dr; +- new_dr = NULL; ++ new_res = NULL; + } + spin_unlock_irqrestore(&dev->devres_lock, flags); +- devres_free(new_dr); ++ devres_free(new_res); + + return dr->data; + } +diff --git a/drivers/base/platform.c b/drivers/base/platform.c +index 063f0ab15259..f80aaaf9f610 100644 +--- a/drivers/base/platform.c ++++ b/drivers/base/platform.c +@@ -375,9 +375,7 @@ int platform_device_add(struct platform_device *pdev) + + while (--i >= 0) { + struct resource *r = &pdev->resource[i]; +- unsigned long type = resource_type(r); +- +- if (type == IORESOURCE_MEM || type == IORESOURCE_IO) ++ if (r->parent) + release_resource(r); + } + +@@ -408,9 +406,7 @@ void platform_device_del(struct platform_device *pdev) + + for (i = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; +- unsigned long type = resource_type(r); +- +- if (type == IORESOURCE_MEM || type == IORESOURCE_IO) ++ if (r->parent) + release_resource(r); + } + } +diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c +index acef9f9f759a..652b5a367c1f 100644 +--- a/drivers/base/power/clock_ops.c ++++ b/drivers/base/power/clock_ops.c +@@ -38,7 +38,7 @@ struct pm_clock_entry { + * @dev: The device for the given clock + * @ce: PM clock entry corresponding to the clock. + */ +-static inline int __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) ++static inline void __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) + { + int ret; + +@@ -50,8 +50,6 @@ static inline int __pm_clk_enable(struct device *dev, struct pm_clock_entry *ce) + dev_err(dev, "%s: failed to enable clk %p, error %d\n", + __func__, ce->clk, ret); + } +- +- return ret; + } + + /** +diff --git a/drivers/clk/pistachio/clk-pistachio.c b/drivers/clk/pistachio/clk-pistachio.c +index 8c0fe8828f99..c4ceb5eaf46c 100644 +--- a/drivers/clk/pistachio/clk-pistachio.c ++++ b/drivers/clk/pistachio/clk-pistachio.c +@@ -159,9 +159,15 @@ PNAME(mux_debug) = { "mips_pll_mux", "rpu_v_pll_mux", + "wifi_pll_mux", "bt_pll_mux" }; + static u32 mux_debug_idx[] = { 0x0, 0x1, 0x2, 0x4, 0x8, 0x10 }; + +-static unsigned int pistachio_critical_clks[] __initdata = { +- CLK_MIPS, +- CLK_PERIPH_SYS, ++static unsigned int pistachio_critical_clks_core[] __initdata = { ++ CLK_MIPS ++}; ++ ++static unsigned int pistachio_critical_clks_sys[] __initdata = { ++ PERIPH_CLK_SYS, ++ PERIPH_CLK_SYS_BUS, ++ PERIPH_CLK_DDR, ++ PERIPH_CLK_ROM, + }; + + static void __init pistachio_clk_init(struct device_node *np) +@@ -193,8 +199,8 @@ static void __init pistachio_clk_init(struct device_node *np) + + pistachio_clk_register_provider(p); + +- pistachio_clk_force_enable(p, pistachio_critical_clks, +- ARRAY_SIZE(pistachio_critical_clks)); ++ pistachio_clk_force_enable(p, pistachio_critical_clks_core, ++ ARRAY_SIZE(pistachio_critical_clks_core)); + } + CLK_OF_DECLARE(pistachio_clk, "img,pistachio-clk", pistachio_clk_init); + +@@ -261,6 +267,9 @@ static void __init pistachio_clk_periph_init(struct device_node *np) + ARRAY_SIZE(pistachio_periph_gates)); + + pistachio_clk_register_provider(p); ++ ++ pistachio_clk_force_enable(p, pistachio_critical_clks_sys, ++ ARRAY_SIZE(pistachio_critical_clks_sys)); + } + CLK_OF_DECLARE(pistachio_clk_periph, "img,pistachio-clk-periph", + pistachio_clk_periph_init); +diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c +index e17dada0dd21..c9b459821084 100644 +--- a/drivers/clk/pistachio/clk-pll.c ++++ b/drivers/clk/pistachio/clk-pll.c +@@ -65,6 +65,12 @@ + #define MIN_OUTPUT_FRAC 12000000UL + #define MAX_OUTPUT_FRAC 1600000000UL + ++/* Fractional PLL operating modes */ ++enum pll_mode { ++ PLL_MODE_FRAC, ++ PLL_MODE_INT, ++}; ++ + struct pistachio_clk_pll { + struct clk_hw hw; + void __iomem *base; +@@ -88,12 +94,10 @@ static inline void pll_lock(struct pistachio_clk_pll *pll) + cpu_relax(); + } + +-static inline u32 do_div_round_closest(u64 dividend, u32 divisor) ++static inline u64 do_div_round_closest(u64 dividend, u64 divisor) + { + dividend += divisor / 2; +- do_div(dividend, divisor); +- +- return dividend; ++ return div64_u64(dividend, divisor); + } + + static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) +@@ -101,6 +105,29 @@ static inline struct pistachio_clk_pll *to_pistachio_pll(struct clk_hw *hw) + return container_of(hw, struct pistachio_clk_pll, hw); + } + ++static inline enum pll_mode pll_frac_get_mode(struct clk_hw *hw) ++{ ++ struct pistachio_clk_pll *pll = to_pistachio_pll(hw); ++ u32 val; ++ ++ val = pll_readl(pll, PLL_CTRL3) & PLL_FRAC_CTRL3_DSMPD; ++ return val ? PLL_MODE_INT : PLL_MODE_FRAC; ++} ++ ++static inline void pll_frac_set_mode(struct clk_hw *hw, enum pll_mode mode) ++{ ++ struct pistachio_clk_pll *pll = to_pistachio_pll(hw); ++ u32 val; ++ ++ val = pll_readl(pll, PLL_CTRL3); ++ if (mode == PLL_MODE_INT) ++ val |= PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD; ++ else ++ val &= ~(PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_DACPD); ++ ++ pll_writel(pll, val, PLL_CTRL3); ++} ++ + static struct pistachio_pll_rate_table * + pll_get_params(struct pistachio_clk_pll *pll, unsigned long fref, + unsigned long fout) +@@ -136,8 +163,7 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw) + u32 val; + + val = pll_readl(pll, PLL_CTRL3); +- val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_DACPD | +- PLL_FRAC_CTRL3_DSMPD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | ++ val &= ~(PLL_FRAC_CTRL3_PD | PLL_FRAC_CTRL3_FOUTPOSTDIVPD | + PLL_FRAC_CTRL3_FOUT4PHASEPD | PLL_FRAC_CTRL3_FOUTVCOPD); + pll_writel(pll, val, PLL_CTRL3); + +@@ -173,7 +199,7 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); + struct pistachio_pll_rate_table *params; + int enabled = pll_gf40lp_frac_is_enabled(hw); +- u32 val, vco, old_postdiv1, old_postdiv2; ++ u64 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) +@@ -183,17 +209,21 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, + if (!params || !params->refdiv) + return -EINVAL; + +- vco = params->fref * params->fbdiv / params->refdiv; ++ /* calculate vco */ ++ vco = params->fref; ++ vco *= (params->fbdiv << 24) + params->frac; ++ vco = div64_u64(vco, params->refdiv << 24); ++ + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) +- pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, ++ pr_warn("%s: VCO %llu is out of range %lu..%lu\n", name, vco, + MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); + +- val = params->fref / params->refdiv; ++ val = div64_u64(params->fref, params->refdiv); + if (val < MIN_PFD) +- pr_warn("%s: PFD %u is too low (min %lu)\n", ++ pr_warn("%s: PFD %llu is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) +- pr_warn("%s: PFD %u is too high (max %u)\n", ++ pr_warn("%s: PFD %llu is too high (max %llu)\n", + name, val, vco / 16); + + val = pll_readl(pll, PLL_CTRL1); +@@ -227,6 +257,12 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, + (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); + pll_writel(pll, val, PLL_CTRL2); + ++ /* set operating mode */ ++ if (params->frac) ++ pll_frac_set_mode(hw, PLL_MODE_FRAC); ++ else ++ pll_frac_set_mode(hw, PLL_MODE_INT); ++ + if (enabled) + pll_lock(pll); + +@@ -237,8 +273,7 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) + { + struct pistachio_clk_pll *pll = to_pistachio_pll(hw); +- u32 val, prediv, fbdiv, frac, postdiv1, postdiv2; +- u64 rate = parent_rate; ++ u64 val, prediv, fbdiv, frac, postdiv1, postdiv2, rate; + + val = pll_readl(pll, PLL_CTRL1); + prediv = (val >> PLL_CTRL1_REFDIV_SHIFT) & PLL_CTRL1_REFDIV_MASK; +@@ -251,7 +286,13 @@ static unsigned long pll_gf40lp_frac_recalc_rate(struct clk_hw *hw, + PLL_FRAC_CTRL2_POSTDIV2_MASK; + frac = (val >> PLL_FRAC_CTRL2_FRAC_SHIFT) & PLL_FRAC_CTRL2_FRAC_MASK; + +- rate *= (fbdiv << 24) + frac; ++ /* get operating mode (int/frac) and calculate rate accordingly */ ++ rate = parent_rate; ++ if (pll_frac_get_mode(hw) == PLL_MODE_FRAC) ++ rate *= (fbdiv << 24) + frac; ++ else ++ rate *= (fbdiv << 24); ++ + rate = do_div_round_closest(rate, (prediv * postdiv1 * postdiv2) << 24); + + return rate; +@@ -279,7 +320,7 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw) + u32 val; + + val = pll_readl(pll, PLL_CTRL1); +- val &= ~(PLL_INT_CTRL1_PD | PLL_INT_CTRL1_DSMPD | ++ val &= ~(PLL_INT_CTRL1_PD | + PLL_INT_CTRL1_FOUTPOSTDIVPD | PLL_INT_CTRL1_FOUTVCOPD); + pll_writel(pll, val, PLL_CTRL1); + +@@ -325,12 +366,12 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, + if (!params || !params->refdiv) + return -EINVAL; + +- vco = params->fref * params->fbdiv / params->refdiv; ++ vco = div_u64(params->fref * params->fbdiv, params->refdiv); + if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_LA, MAX_VCO_LA); + +- val = params->fref / params->refdiv; ++ val = div_u64(params->fref, params->refdiv); + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); +diff --git a/drivers/clk/pistachio/clk.h b/drivers/clk/pistachio/clk.h +index 52fabbc24624..8d45178dbde3 100644 +--- a/drivers/clk/pistachio/clk.h ++++ b/drivers/clk/pistachio/clk.h +@@ -95,13 +95,13 @@ struct pistachio_fixed_factor { + } + + struct pistachio_pll_rate_table { +- unsigned long fref; +- unsigned long fout; +- unsigned int refdiv; +- unsigned int fbdiv; +- unsigned int postdiv1; +- unsigned int postdiv2; +- unsigned int frac; ++ unsigned long long fref; ++ unsigned long long fout; ++ unsigned long long refdiv; ++ unsigned long long fbdiv; ++ unsigned long long postdiv1; ++ unsigned long long postdiv2; ++ unsigned long long frac; + }; + + enum pistachio_pll_type { +diff --git a/drivers/clk/pxa/clk-pxa25x.c b/drivers/clk/pxa/clk-pxa25x.c +index 6cd88d963a7f..542e45ef5087 100644 +--- a/drivers/clk/pxa/clk-pxa25x.c ++++ b/drivers/clk/pxa/clk-pxa25x.c +@@ -79,7 +79,7 @@ unsigned int pxa25x_get_clk_frequency_khz(int info) + clks[3] / 1000000, (clks[3] % 1000000) / 10000); + } + +- return (unsigned int)clks[0]; ++ return (unsigned int)clks[0] / KHz; + } + + static unsigned long clk_pxa25x_memory_get_rate(struct clk_hw *hw, +diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c +index 9a31b77eed23..5b82d30baf9f 100644 +--- a/drivers/clk/pxa/clk-pxa27x.c ++++ b/drivers/clk/pxa/clk-pxa27x.c +@@ -80,7 +80,7 @@ unsigned int pxa27x_get_clk_frequency_khz(int info) + pr_info("System bus clock: %ld.%02ldMHz\n", + clks[4] / 1000000, (clks[4] % 1000000) / 10000); + } +- return (unsigned int)clks[0]; ++ return (unsigned int)clks[0] / KHz; + } + + bool pxa27x_is_ppll_disabled(void) +diff --git a/drivers/clk/pxa/clk-pxa3xx.c b/drivers/clk/pxa/clk-pxa3xx.c +index ac03ba49e9d1..4af4eed5f89f 100644 +--- a/drivers/clk/pxa/clk-pxa3xx.c ++++ b/drivers/clk/pxa/clk-pxa3xx.c +@@ -78,7 +78,7 @@ unsigned int pxa3xx_get_clk_frequency_khz(int info) + pr_info("System bus clock: %ld.%02ldMHz\n", + clks[4] / 1000000, (clks[4] % 1000000) / 10000); + } +- return (unsigned int)clks[0]; ++ return (unsigned int)clks[0] / KHz; + } + + static unsigned long clk_pxa3xx_ac97_get_rate(struct clk_hw *hw, +diff --git a/drivers/clk/qcom/gcc-apq8084.c b/drivers/clk/qcom/gcc-apq8084.c +index 54a756b90a37..457c540585f9 100644 +--- a/drivers/clk/qcom/gcc-apq8084.c ++++ b/drivers/clk/qcom/gcc-apq8084.c +@@ -2105,6 +2105,7 @@ static struct clk_branch gcc_ce1_clk = { + "ce1_clk_src", + }, + .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c +index c66f7bc2ae87..5d75bffab141 100644 +--- a/drivers/clk/qcom/gcc-msm8916.c ++++ b/drivers/clk/qcom/gcc-msm8916.c +@@ -2278,7 +2278,7 @@ static struct clk_branch gcc_prng_ahb_clk = { + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x45004, +- .enable_mask = BIT(0), ++ .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "gcc_prng_ahb_clk", + .parent_names = (const char *[]){ +diff --git a/drivers/clk/qcom/gcc-msm8974.c b/drivers/clk/qcom/gcc-msm8974.c +index c39d09874e74..f06a082e3e87 100644 +--- a/drivers/clk/qcom/gcc-msm8974.c ++++ b/drivers/clk/qcom/gcc-msm8974.c +@@ -1783,6 +1783,7 @@ static struct clk_branch gcc_ce1_clk = { + "ce1_clk_src", + }, + .num_parents = 1, ++ .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c +index 4f817ed9e6ee..0211162ee879 100644 +--- a/drivers/clk/rockchip/clk-rk3288.c ++++ b/drivers/clk/rockchip/clk-rk3288.c +@@ -578,7 +578,7 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { + COMPOSITE(0, "mac_pll_src", mux_pll_src_npll_cpll_gpll_p, 0, + RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS, + RK3288_CLKGATE_CON(2), 5, GFLAGS), +- MUX(SCLK_MAC, "mac_clk", mux_mac_p, 0, ++ MUX(SCLK_MAC, "mac_clk", mux_mac_p, CLK_SET_RATE_PARENT, + RK3288_CLKSEL_CON(21), 4, 1, MFLAGS), + GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 0, + RK3288_CLKGATE_CON(5), 3, GFLAGS), +diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c +index cae2c048488d..d1af2fc53c5f 100644 +--- a/drivers/clk/samsung/clk-exynos4.c ++++ b/drivers/clk/samsung/clk-exynos4.c +@@ -86,6 +86,7 @@ + #define DIV_PERIL4 0xc560 + #define DIV_PERIL5 0xc564 + #define E4X12_DIV_CAM1 0xc568 ++#define E4X12_GATE_BUS_FSYS1 0xc744 + #define GATE_SCLK_CAM 0xc820 + #define GATE_IP_CAM 0xc920 + #define GATE_IP_TV 0xc924 +@@ -1097,6 +1098,7 @@ static struct samsung_gate_clock exynos4x12_gate_clks[] __initdata = { + 0), + GATE(CLK_PPMUIMAGE, "ppmuimage", "aclk200", E4X12_GATE_IP_IMAGE, 9, 0, + 0), ++ GATE(CLK_TSADC, "tsadc", "aclk133", E4X12_GATE_BUS_FSYS1, 16, 0, 0), + GATE(CLK_MIPI_HSI, "mipi_hsi", "aclk133", GATE_IP_FSYS, 10, 0, 0), + GATE(CLK_CHIPID, "chipid", "aclk100", E4X12_GATE_IP_PERIR, 0, 0, 0), + GATE(CLK_SYSREG, "sysreg", "aclk100", E4X12_GATE_IP_PERIR, 1, +diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c +index cf7e8fa7b624..793cb1d2f7ae 100644 +--- a/drivers/clk/samsung/clk-s5pv210.c ++++ b/drivers/clk/samsung/clk-s5pv210.c +@@ -828,6 +828,8 @@ static void __init __s5pv210_clk_init(struct device_node *np, + + s5pv210_clk_sleep_init(); + ++ samsung_clk_of_add_provider(np, ctx); ++ + pr_info("%s clocks: mout_apll = %ld, mout_mpll = %ld\n" + "\tmout_epll = %ld, mout_vpll = %ld\n", + is_s5p6442 ? "S5P6442" : "S5PV210", +diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c +index a96dd8e53fdb..b674ffc4f5ce 100644 +--- a/drivers/clk/versatile/clk-sp810.c ++++ b/drivers/clk/versatile/clk-sp810.c +@@ -128,8 +128,8 @@ static struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec, + { + struct clk_sp810 *sp810 = data; + +- if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] > +- ARRAY_SIZE(sp810->timerclken))) ++ if (WARN_ON(clkspec->args_count != 1 || ++ clkspec->args[0] >= ARRAY_SIZE(sp810->timerclken))) + return NULL; + + return sp810->timerclken[clkspec->args[0]].clk; +diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c +index 7adae42a7b79..ed3838781b4c 100644 +--- a/drivers/crypto/vmx/aes_ctr.c ++++ b/drivers/crypto/vmx/aes_ctr.c +@@ -113,6 +113,7 @@ static int p8_aes_ctr_crypt(struct blkcipher_desc *desc, + struct scatterlist *src, unsigned int nbytes) + { + int ret; ++ u64 inc; + struct blkcipher_walk walk; + struct p8_aes_ctr_ctx *ctx = + crypto_tfm_ctx(crypto_blkcipher_tfm(desc->tfm)); +@@ -140,7 +141,12 @@ static int p8_aes_ctr_crypt(struct blkcipher_desc *desc, + walk.iv); + pagefault_enable(); + +- crypto_inc(walk.iv, AES_BLOCK_SIZE); ++ /* We need to update IV mostly for last bytes/round */ ++ inc = (nbytes & AES_BLOCK_MASK) / AES_BLOCK_SIZE; ++ if (inc > 0) ++ while (inc--) ++ crypto_inc(walk.iv, AES_BLOCK_SIZE); ++ + nbytes &= AES_BLOCK_SIZE - 1; + ret = blkcipher_walk_done(desc, &walk, nbytes); + } +diff --git a/drivers/crypto/vmx/aesp8-ppc.pl b/drivers/crypto/vmx/aesp8-ppc.pl +index 6c5c20c6108e..228053921b3f 100644 +--- a/drivers/crypto/vmx/aesp8-ppc.pl ++++ b/drivers/crypto/vmx/aesp8-ppc.pl +@@ -1437,28 +1437,28 @@ Load_ctr32_enc_key: + ?vperm v31,v31,$out0,$keyperm + lvx v25,$x10,$key_ # pre-load round[2] + +- vadduwm $two,$one,$one ++ vadduqm $two,$one,$one + subi $inp,$inp,15 # undo "caller" + $SHL $len,$len,4 + +- vadduwm $out1,$ivec,$one # counter values ... +- vadduwm $out2,$ivec,$two ++ vadduqm $out1,$ivec,$one # counter values ... ++ vadduqm $out2,$ivec,$two + vxor $out0,$ivec,$rndkey0 # ... xored with rndkey[0] + le?li $idx,8 +- vadduwm $out3,$out1,$two ++ vadduqm $out3,$out1,$two + vxor $out1,$out1,$rndkey0 + le?lvsl $inpperm,0,$idx +- vadduwm $out4,$out2,$two ++ vadduqm $out4,$out2,$two + vxor $out2,$out2,$rndkey0 + le?vspltisb $tmp,0x0f +- vadduwm $out5,$out3,$two ++ vadduqm $out5,$out3,$two + vxor $out3,$out3,$rndkey0 + le?vxor $inpperm,$inpperm,$tmp # transform for lvx_u/stvx_u +- vadduwm $out6,$out4,$two ++ vadduqm $out6,$out4,$two + vxor $out4,$out4,$rndkey0 +- vadduwm $out7,$out5,$two ++ vadduqm $out7,$out5,$two + vxor $out5,$out5,$rndkey0 +- vadduwm $ivec,$out6,$two # next counter value ++ vadduqm $ivec,$out6,$two # next counter value + vxor $out6,$out6,$rndkey0 + vxor $out7,$out7,$rndkey0 + +@@ -1594,27 +1594,27 @@ Loop_ctr32_enc8x_middle: + + vcipherlast $in0,$out0,$in0 + vcipherlast $in1,$out1,$in1 +- vadduwm $out1,$ivec,$one # counter values ... ++ vadduqm $out1,$ivec,$one # counter values ... + vcipherlast $in2,$out2,$in2 +- vadduwm $out2,$ivec,$two ++ vadduqm $out2,$ivec,$two + vxor $out0,$ivec,$rndkey0 # ... xored with rndkey[0] + vcipherlast $in3,$out3,$in3 +- vadduwm $out3,$out1,$two ++ vadduqm $out3,$out1,$two + vxor $out1,$out1,$rndkey0 + vcipherlast $in4,$out4,$in4 +- vadduwm $out4,$out2,$two ++ vadduqm $out4,$out2,$two + vxor $out2,$out2,$rndkey0 + vcipherlast $in5,$out5,$in5 +- vadduwm $out5,$out3,$two ++ vadduqm $out5,$out3,$two + vxor $out3,$out3,$rndkey0 + vcipherlast $in6,$out6,$in6 +- vadduwm $out6,$out4,$two ++ vadduqm $out6,$out4,$two + vxor $out4,$out4,$rndkey0 + vcipherlast $in7,$out7,$in7 +- vadduwm $out7,$out5,$two ++ vadduqm $out7,$out5,$two + vxor $out5,$out5,$rndkey0 + le?vperm $in0,$in0,$in0,$inpperm +- vadduwm $ivec,$out6,$two # next counter value ++ vadduqm $ivec,$out6,$two # next counter value + vxor $out6,$out6,$rndkey0 + le?vperm $in1,$in1,$in1,$inpperm + vxor $out7,$out7,$rndkey0 +diff --git a/drivers/crypto/vmx/ghashp8-ppc.pl b/drivers/crypto/vmx/ghashp8-ppc.pl +index 0a6f899839dd..d8429cb71f02 100644 +--- a/drivers/crypto/vmx/ghashp8-ppc.pl ++++ b/drivers/crypto/vmx/ghashp8-ppc.pl +@@ -61,6 +61,12 @@ $code=<<___; + mtspr 256,r0 + li r10,0x30 + lvx_u $H,0,r4 # load H ++ le?xor r7,r7,r7 ++ le?addi r7,r7,0x8 # need a vperm start with 08 ++ le?lvsr 5,0,r7 ++ le?vspltisb 6,0x0f ++ le?vxor 5,5,6 # set a b-endian mask ++ le?vperm $H,$H,$H,5 + + vspltisb $xC2,-16 # 0xf0 + vspltisb $t0,1 # one +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +index 27df17a0e620..89c3dd62ba21 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +@@ -75,6 +75,11 @@ void amdgpu_connector_hotplug(struct drm_connector *connector) + if (!amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd)) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + } else if (amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) { ++ /* Don't try to start link training before we ++ * have the dpcd */ ++ if (!amdgpu_atombios_dp_get_dpcd(amdgpu_connector)) ++ return; ++ + /* set it to OFF so that drm_helper_connector_dpms() + * won't return immediately since the current state + * is ON at this point. +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c +index db5422e65ec5..a8207e5a8549 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c +@@ -97,18 +97,12 @@ int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size, + /* add 8 bytes for the rptr/wptr shadows and + * add them to the end of the ring allocation. + */ +- adev->irq.ih.ring = kzalloc(adev->irq.ih.ring_size + 8, GFP_KERNEL); ++ adev->irq.ih.ring = pci_alloc_consistent(adev->pdev, ++ adev->irq.ih.ring_size + 8, ++ &adev->irq.ih.rb_dma_addr); + if (adev->irq.ih.ring == NULL) + return -ENOMEM; +- adev->irq.ih.rb_dma_addr = pci_map_single(adev->pdev, +- (void *)adev->irq.ih.ring, +- adev->irq.ih.ring_size, +- PCI_DMA_BIDIRECTIONAL); +- if (pci_dma_mapping_error(adev->pdev, adev->irq.ih.rb_dma_addr)) { +- dev_err(&adev->pdev->dev, "Failed to DMA MAP the IH RB page\n"); +- kfree((void *)adev->irq.ih.ring); +- return -ENOMEM; +- } ++ memset((void *)adev->irq.ih.ring, 0, adev->irq.ih.ring_size + 8); + adev->irq.ih.wptr_offs = (adev->irq.ih.ring_size / 4) + 0; + adev->irq.ih.rptr_offs = (adev->irq.ih.ring_size / 4) + 1; + } +@@ -148,9 +142,9 @@ void amdgpu_ih_ring_fini(struct amdgpu_device *adev) + /* add 8 bytes for the rptr/wptr shadows and + * add them to the end of the ring allocation. + */ +- pci_unmap_single(adev->pdev, adev->irq.ih.rb_dma_addr, +- adev->irq.ih.ring_size + 8, PCI_DMA_BIDIRECTIONAL); +- kfree((void *)adev->irq.ih.ring); ++ pci_free_consistent(adev->pdev, adev->irq.ih.ring_size + 8, ++ (void *)adev->irq.ih.ring, ++ adev->irq.ih.rb_dma_addr); + adev->irq.ih.ring = NULL; + } + } else { +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +index f5c22556ec2c..2abc661845b6 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +@@ -374,7 +374,8 @@ static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) + unsigned height_in_mb = ALIGN(height / 16, 2); + unsigned fs_in_mb = width_in_mb * height_in_mb; + +- unsigned image_size, tmp, min_dpb_size, num_dpb_buffer, min_ctx_size; ++ unsigned image_size, tmp, min_dpb_size, num_dpb_buffer; ++ unsigned min_ctx_size = 0; + + image_size = width * height; + image_size += image_size / 2; +diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c +index 9ba0a7d5bc8e..92b6acadfc52 100644 +--- a/drivers/gpu/drm/amd/amdgpu/atombios_dp.c ++++ b/drivers/gpu/drm/amd/amdgpu/atombios_dp.c +@@ -139,7 +139,8 @@ amdgpu_atombios_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *m + + tx_buf[0] = msg->address & 0xff; + tx_buf[1] = msg->address >> 8; +- tx_buf[2] = msg->request << 4; ++ tx_buf[2] = (msg->request << 4) | ++ ((msg->address >> 16) & 0xf); + tx_buf[3] = msg->size ? (msg->size - 1) : 0; + + switch (msg->request & ~DP_AUX_I2C_MOT) { +diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +index e70a26f587a0..e774a437dd65 100644 +--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +@@ -1331,7 +1331,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, + tmp = REG_SET_FIELD(wm_mask, DPG_WATERMARK_MASK_CONTROL, URGENCY_WATERMARK_MASK, 2); + WREG32(mmDPG_WATERMARK_MASK_CONTROL + amdgpu_crtc->crtc_offset, tmp); + tmp = RREG32(mmDPG_PIPE_URGENCY_CONTROL + amdgpu_crtc->crtc_offset); +- tmp = REG_SET_FIELD(tmp, DPG_PIPE_URGENCY_CONTROL, URGENCY_LOW_WATERMARK, latency_watermark_a); ++ tmp = REG_SET_FIELD(tmp, DPG_PIPE_URGENCY_CONTROL, URGENCY_LOW_WATERMARK, latency_watermark_b); + tmp = REG_SET_FIELD(tmp, DPG_PIPE_URGENCY_CONTROL, URGENCY_HIGH_WATERMARK, line_time); + WREG32(mmDPG_PIPE_URGENCY_CONTROL + amdgpu_crtc->crtc_offset, tmp); + /* restore original selection */ +diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +index dcb402ee048a..c4a21a7afd68 100644 +--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +@@ -1329,7 +1329,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, + tmp = REG_SET_FIELD(wm_mask, DPG_WATERMARK_MASK_CONTROL, URGENCY_WATERMARK_MASK, 2); + WREG32(mmDPG_WATERMARK_MASK_CONTROL + amdgpu_crtc->crtc_offset, tmp); + tmp = RREG32(mmDPG_PIPE_URGENCY_CONTROL + amdgpu_crtc->crtc_offset); +- tmp = REG_SET_FIELD(tmp, DPG_PIPE_URGENCY_CONTROL, URGENCY_LOW_WATERMARK, latency_watermark_a); ++ tmp = REG_SET_FIELD(tmp, DPG_PIPE_URGENCY_CONTROL, URGENCY_LOW_WATERMARK, latency_watermark_b); + tmp = REG_SET_FIELD(tmp, DPG_PIPE_URGENCY_CONTROL, URGENCY_HIGH_WATERMARK, line_time); + WREG32(mmDPG_PIPE_URGENCY_CONTROL + amdgpu_crtc->crtc_offset, tmp); + /* restore original selection */ +diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c +index 884b4f9b81c4..603146ec9868 100644 +--- a/drivers/gpu/drm/i915/i915_drv.c ++++ b/drivers/gpu/drm/i915/i915_drv.c +@@ -683,15 +683,18 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation) + + pci_disable_device(drm_dev->pdev); + /* +- * During hibernation on some GEN4 platforms the BIOS may try to access ++ * During hibernation on some platforms the BIOS may try to access + * the device even though it's already in D3 and hang the machine. So + * leave the device in D0 on those platforms and hope the BIOS will +- * power down the device properly. Platforms where this was seen: +- * Lenovo Thinkpad X301, X61s ++ * power down the device properly. The issue was seen on multiple old ++ * GENs with different BIOS vendors, so having an explicit blacklist ++ * is inpractical; apply the workaround on everything pre GEN6. The ++ * platforms where the issue was seen: ++ * Lenovo Thinkpad X301, X61s, X60, T60, X41 ++ * Fujitsu FSC S7110 ++ * Acer Aspire 1830T + */ +- if (!(hibernation && +- drm_dev->pdev->subsystem_vendor == PCI_VENDOR_ID_LENOVO && +- INTEL_INFO(dev_priv)->gen == 4)) ++ if (!(hibernation && INTEL_INFO(dev_priv)->gen < 6)) + pci_set_power_state(drm_dev->pdev, PCI_D3hot); + + return 0; +diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h +index fd1de451c8c6..e1df8feb05be 100644 +--- a/drivers/gpu/drm/i915/i915_drv.h ++++ b/drivers/gpu/drm/i915/i915_drv.h +@@ -3303,13 +3303,13 @@ int intel_freq_opcode(struct drm_i915_private *dev_priv, int val); + #define I915_READ64(reg) dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true) + + #define I915_READ64_2x32(lower_reg, upper_reg) ({ \ +- u32 upper, lower, tmp; \ +- tmp = I915_READ(upper_reg); \ ++ u32 upper, lower, old_upper, loop = 0; \ ++ upper = I915_READ(upper_reg); \ + do { \ +- upper = tmp; \ ++ old_upper = upper; \ + lower = I915_READ(lower_reg); \ +- tmp = I915_READ(upper_reg); \ +- } while (upper != tmp); \ ++ upper = I915_READ(upper_reg); \ ++ } while (upper != old_upper && loop++ < 2); \ + (u64)upper << 32 | lower; }) + + #define POSTING_READ(reg) (void)I915_READ_NOTRACE(reg) +diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c +index a7fa14516cda..5e6b4a29e503 100644 +--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c ++++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c +@@ -1024,6 +1024,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas, + u32 old_read = obj->base.read_domains; + u32 old_write = obj->base.write_domain; + ++ obj->dirty = 1; /* be paranoid */ + obj->base.write_domain = obj->base.pending_write_domain; + if (obj->base.write_domain == 0) + obj->base.pending_read_domains |= obj->base.read_domains; +@@ -1031,7 +1032,6 @@ i915_gem_execbuffer_move_to_active(struct list_head *vmas, + + i915_vma_move_to_active(vma, ring); + if (obj->base.write_domain) { +- obj->dirty = 1; + i915_gem_request_assign(&obj->last_write_req, req); + + intel_fb_obj_invalidate(obj, ring, ORIGIN_CS); +diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c +index bcb41e61877d..fb842d6e343f 100644 +--- a/drivers/gpu/drm/i915/intel_csr.c ++++ b/drivers/gpu/drm/i915/intel_csr.c +@@ -350,7 +350,7 @@ static void finish_csr_load(const struct firmware *fw, void *context) + } + csr->mmio_count = dmc_header->mmio_count; + for (i = 0; i < dmc_header->mmio_count; i++) { +- if (dmc_header->mmioaddr[i] < CSR_MMIO_START_RANGE && ++ if (dmc_header->mmioaddr[i] < CSR_MMIO_START_RANGE || + dmc_header->mmioaddr[i] > CSR_MMIO_END_RANGE) { + DRM_ERROR(" Firmware has wrong mmio address 0x%x\n", + dmc_header->mmioaddr[i]); +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index 87476ff181dd..107c6c0519fd 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -14665,6 +14665,24 @@ void intel_modeset_init(struct drm_device *dev) + if (INTEL_INFO(dev)->num_pipes == 0) + return; + ++ /* ++ * There may be no VBT; and if the BIOS enabled SSC we can ++ * just keep using it to avoid unnecessary flicker. Whereas if the ++ * BIOS isn't using it, don't assume it will work even if the VBT ++ * indicates as much. ++ */ ++ if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { ++ bool bios_lvds_use_ssc = !!(I915_READ(PCH_DREF_CONTROL) & ++ DREF_SSC1_ENABLE); ++ ++ if (dev_priv->vbt.lvds_use_ssc != bios_lvds_use_ssc) { ++ DRM_DEBUG_KMS("SSC %sabled by BIOS, overriding VBT which says %sabled\n", ++ bios_lvds_use_ssc ? "en" : "dis", ++ dev_priv->vbt.lvds_use_ssc ? "en" : "dis"); ++ dev_priv->vbt.lvds_use_ssc = bios_lvds_use_ssc; ++ } ++ } ++ + intel_init_display(dev); + intel_init_audio(dev); + +@@ -15160,7 +15178,6 @@ void intel_modeset_setup_hw_state(struct drm_device *dev, + + void intel_modeset_gem_init(struct drm_device *dev) + { +- struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *c; + struct drm_i915_gem_object *obj; + int ret; +@@ -15169,16 +15186,6 @@ void intel_modeset_gem_init(struct drm_device *dev) + intel_init_gt_powersave(dev); + mutex_unlock(&dev->struct_mutex); + +- /* +- * There may be no VBT; and if the BIOS enabled SSC we can +- * just keep using it to avoid unnecessary flicker. Whereas if the +- * BIOS isn't using it, don't assume it will work even if the VBT +- * indicates as much. +- */ +- if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) +- dev_priv->vbt.lvds_use_ssc = !!(I915_READ(PCH_DREF_CONTROL) & +- DREF_SSC1_ENABLE); +- + intel_modeset_init_hw(dev); + + intel_setup_overlay(dev); +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index 1df0e1fe235f..bd8f8863eb0e 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -4987,9 +4987,12 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) + + intel_dp_probe_oui(intel_dp); + +- if (!intel_dp_probe_mst(intel_dp)) ++ if (!intel_dp_probe_mst(intel_dp)) { ++ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); ++ intel_dp_check_link_status(intel_dp); ++ drm_modeset_unlock(&dev->mode_config.connection_mutex); + goto mst_fail; +- ++ } + } else { + if (intel_dp->is_mst) { + if (intel_dp_check_mst_status(intel_dp) == -EINVAL) +@@ -4997,10 +5000,6 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) + } + + if (!intel_dp->is_mst) { +- /* +- * we'll check the link status via the normal hot plug path later - +- * but for short hpds we should check it now +- */ + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + intel_dp_check_link_status(intel_dp); + drm_modeset_unlock(&dev->mode_config.connection_mutex); +diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c +index b5a5558ecd63..68b25dd525f0 100644 +--- a/drivers/gpu/drm/i915/intel_dsi.c ++++ b/drivers/gpu/drm/i915/intel_dsi.c +@@ -1036,11 +1036,7 @@ void intel_dsi_init(struct drm_device *dev) + intel_connector->unregister = intel_connector_unregister; + + /* Pipe A maps to MIPI DSI port A, pipe B maps to MIPI DSI port C */ +- if (dev_priv->vbt.dsi.config->dual_link) { +- /* XXX: does dual link work on either pipe? */ +- intel_encoder->crtc_mask = (1 << PIPE_A); +- intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C)); +- } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { ++ if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIA) { + intel_encoder->crtc_mask = (1 << PIPE_A); + intel_dsi->ports = (1 << PORT_A); + } else if (dev_priv->vbt.dsi.port == DVO_PORT_MIPIC) { +@@ -1048,6 +1044,9 @@ void intel_dsi_init(struct drm_device *dev) + intel_dsi->ports = (1 << PORT_C); + } + ++ if (dev_priv->vbt.dsi.config->dual_link) ++ intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C)); ++ + /* Create a DSI host (and a device) for each port. */ + for_each_dsi_port(port, intel_dsi->ports) { + struct intel_dsi_host *host; +diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c +index a8dbb3ef4e3c..7c6225c84ba6 100644 +--- a/drivers/gpu/drm/qxl/qxl_display.c ++++ b/drivers/gpu/drm/qxl/qxl_display.c +@@ -160,9 +160,35 @@ static int qxl_add_monitors_config_modes(struct drm_connector *connector, + *pwidth = head->width; + *pheight = head->height; + drm_mode_probed_add(connector, mode); ++ /* remember the last custom size for mode validation */ ++ qdev->monitors_config_width = mode->hdisplay; ++ qdev->monitors_config_height = mode->vdisplay; + return 1; + } + ++static struct mode_size { ++ int w; ++ int h; ++} common_modes[] = { ++ { 640, 480}, ++ { 720, 480}, ++ { 800, 600}, ++ { 848, 480}, ++ {1024, 768}, ++ {1152, 768}, ++ {1280, 720}, ++ {1280, 800}, ++ {1280, 854}, ++ {1280, 960}, ++ {1280, 1024}, ++ {1440, 900}, ++ {1400, 1050}, ++ {1680, 1050}, ++ {1600, 1200}, ++ {1920, 1080}, ++ {1920, 1200} ++}; ++ + static int qxl_add_common_modes(struct drm_connector *connector, + unsigned pwidth, + unsigned pheight) +@@ -170,29 +196,6 @@ static int qxl_add_common_modes(struct drm_connector *connector, + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode = NULL; + int i; +- struct mode_size { +- int w; +- int h; +- } common_modes[] = { +- { 640, 480}, +- { 720, 480}, +- { 800, 600}, +- { 848, 480}, +- {1024, 768}, +- {1152, 768}, +- {1280, 720}, +- {1280, 800}, +- {1280, 854}, +- {1280, 960}, +- {1280, 1024}, +- {1440, 900}, +- {1400, 1050}, +- {1680, 1050}, +- {1600, 1200}, +- {1920, 1080}, +- {1920, 1200} +- }; +- + for (i = 0; i < ARRAY_SIZE(common_modes); i++) { + mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, + 60, false, false, false); +@@ -823,11 +826,22 @@ static int qxl_conn_get_modes(struct drm_connector *connector) + static int qxl_conn_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) + { ++ struct drm_device *ddev = connector->dev; ++ struct qxl_device *qdev = ddev->dev_private; ++ int i; ++ + /* TODO: is this called for user defined modes? (xrandr --add-mode) + * TODO: check that the mode fits in the framebuffer */ +- DRM_DEBUG("%s: %dx%d status=%d\n", mode->name, mode->hdisplay, +- mode->vdisplay, mode->status); +- return MODE_OK; ++ ++ if(qdev->monitors_config_width == mode->hdisplay && ++ qdev->monitors_config_height == mode->vdisplay) ++ return MODE_OK; ++ ++ for (i = 0; i < ARRAY_SIZE(common_modes); i++) { ++ if (common_modes[i].w == mode->hdisplay && common_modes[i].h == mode->vdisplay) ++ return MODE_OK; ++ } ++ return MODE_BAD; + } + + static struct drm_encoder *qxl_best_encoder(struct drm_connector *connector) +diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h +index d8549690801d..01a86948eb8c 100644 +--- a/drivers/gpu/drm/qxl/qxl_drv.h ++++ b/drivers/gpu/drm/qxl/qxl_drv.h +@@ -325,6 +325,8 @@ struct qxl_device { + struct work_struct fb_work; + + struct drm_property *hotplug_mode_update_property; ++ int monitors_config_width; ++ int monitors_config_height; + }; + + /* forward declaration for QXL_INFO_IO */ +diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c +index f81e0d7d0232..9cd49c584263 100644 +--- a/drivers/gpu/drm/radeon/atombios_dp.c ++++ b/drivers/gpu/drm/radeon/atombios_dp.c +@@ -171,8 +171,9 @@ radeon_dp_aux_transfer_atom(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) + return -E2BIG; + + tx_buf[0] = msg->address & 0xff; +- tx_buf[1] = msg->address >> 8; +- tx_buf[2] = msg->request << 4; ++ tx_buf[1] = (msg->address >> 8) & 0xff; ++ tx_buf[2] = (msg->request << 4) | ++ ((msg->address >> 16) & 0xf); + tx_buf[3] = msg->size ? (msg->size - 1) : 0; + + switch (msg->request & ~DP_AUX_I2C_MOT) { +diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c +index fbc8d88d6e5d..2c02e99b5f95 100644 +--- a/drivers/gpu/drm/radeon/radeon_audio.c ++++ b/drivers/gpu/drm/radeon/radeon_audio.c +@@ -522,13 +522,15 @@ static int radeon_audio_set_avi_packet(struct drm_encoder *encoder, + return err; + } + +- if (drm_rgb_quant_range_selectable(radeon_connector_edid(connector))) { +- if (radeon_encoder->output_csc == RADEON_OUTPUT_CSC_TVRGB) +- frame.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED; +- else +- frame.quantization_range = HDMI_QUANTIZATION_RANGE_FULL; +- } else { +- frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; ++ if (radeon_encoder->output_csc != RADEON_OUTPUT_CSC_BYPASS) { ++ if (drm_rgb_quant_range_selectable(radeon_connector_edid(connector))) { ++ if (radeon_encoder->output_csc == RADEON_OUTPUT_CSC_TVRGB) ++ frame.quantization_range = HDMI_QUANTIZATION_RANGE_LIMITED; ++ else ++ frame.quantization_range = HDMI_QUANTIZATION_RANGE_FULL; ++ } else { ++ frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT; ++ } + } + + err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); +diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c +index 94b21ae70ef7..5a2cafb4f1bc 100644 +--- a/drivers/gpu/drm/radeon/radeon_connectors.c ++++ b/drivers/gpu/drm/radeon/radeon_connectors.c +@@ -95,6 +95,11 @@ void radeon_connector_hotplug(struct drm_connector *connector) + if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + } else if (radeon_dp_needs_link_train(radeon_connector)) { ++ /* Don't try to start link training before we ++ * have the dpcd */ ++ if (!radeon_dp_getdpcd(radeon_connector)) ++ return; ++ + /* set it to OFF so that drm_helper_connector_dpms() + * won't return immediately since the current state + * is ON at this point. +diff --git a/drivers/gpu/drm/radeon/radeon_dp_auxch.c b/drivers/gpu/drm/radeon/radeon_dp_auxch.c +index fcbd60bb0349..3b0c229d7dcd 100644 +--- a/drivers/gpu/drm/radeon/radeon_dp_auxch.c ++++ b/drivers/gpu/drm/radeon/radeon_dp_auxch.c +@@ -116,8 +116,8 @@ radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg + AUX_SW_WR_BYTES(bytes)); + + /* write the data header into the registers */ +- /* request, addres, msg size */ +- byte = (msg->request << 4); ++ /* request, address, msg size */ ++ byte = (msg->request << 4) | ((msg->address >> 16) & 0xf); + WREG32(AUX_SW_DATA + aux_offset[instance], + AUX_SW_DATA_MASK(byte) | AUX_SW_AUTOINCREMENT_DISABLE); + +diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c +index a2dbbbe0d8d7..39bf74793b8b 100644 +--- a/drivers/hid/hid-cp2112.c ++++ b/drivers/hid/hid-cp2112.c +@@ -537,7 +537,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, + struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; + struct hid_device *hdev = dev->hdev; + u8 buf[64]; +- __be16 word; ++ __le16 word; + ssize_t count; + size_t read_length = 0; + unsigned int retries; +@@ -554,7 +554,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, + if (I2C_SMBUS_READ == read_write) + count = cp2112_read_req(buf, addr, read_length); + else +- count = cp2112_write_req(buf, addr, data->byte, NULL, ++ count = cp2112_write_req(buf, addr, command, NULL, + 0); + break; + case I2C_SMBUS_BYTE_DATA: +@@ -569,7 +569,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, + break; + case I2C_SMBUS_WORD_DATA: + read_length = 2; +- word = cpu_to_be16(data->word); ++ word = cpu_to_le16(data->word); + + if (I2C_SMBUS_READ == read_write) + count = cp2112_write_read_req(buf, addr, read_length, +@@ -582,7 +582,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, + size = I2C_SMBUS_WORD_DATA; + read_write = I2C_SMBUS_READ; + read_length = 2; +- word = cpu_to_be16(data->word); ++ word = cpu_to_le16(data->word); + + count = cp2112_write_read_req(buf, addr, read_length, command, + (u8 *)&word, 2); +@@ -675,7 +675,7 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, + data->byte = buf[0]; + break; + case I2C_SMBUS_WORD_DATA: +- data->word = be16_to_cpup((__be16 *)buf); ++ data->word = le16_to_cpup((__le16 *)buf); + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_length > I2C_SMBUS_BLOCK_MAX) { +diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c +index bfbe1bedda7f..eab5bd6a2442 100644 +--- a/drivers/hid/usbhid/hid-core.c ++++ b/drivers/hid/usbhid/hid-core.c +@@ -164,7 +164,7 @@ static void hid_io_error(struct hid_device *hid) + if (time_after(jiffies, usbhid->stop_retry)) { + + /* Retries failed, so do a port reset unless we lack bandwidth*/ +- if (test_bit(HID_NO_BANDWIDTH, &usbhid->iofl) ++ if (!test_bit(HID_NO_BANDWIDTH, &usbhid->iofl) + && !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { + + schedule_work(&usbhid->reset_work); +diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c +index 13ea1ea23328..bda69a4355fa 100644 +--- a/drivers/iio/accel/mma8452.c ++++ b/drivers/iio/accel/mma8452.c +@@ -229,7 +229,7 @@ static int mma8452_get_hp_filter_index(struct mma8452_data *data, + int i = mma8452_get_odr_index(data); + + return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], +- ARRAY_SIZE(mma8452_scales[0]), val, val2); ++ ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2); + } + + static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) +diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig +index b3d0e94f72eb..8d2439345673 100644 +--- a/drivers/iio/gyro/Kconfig ++++ b/drivers/iio/gyro/Kconfig +@@ -53,7 +53,8 @@ config ADXRS450 + config BMG160 + tristate "BOSCH BMG160 Gyro Sensor" + depends on I2C +- select IIO_TRIGGERED_BUFFER if IIO_BUFFER ++ select IIO_BUFFER ++ select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor + driver. This driver also supports BMI055 gyroscope. +diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c +index 2fd68f2219a7..d42e4fe2c7ed 100644 +--- a/drivers/iio/imu/adis16400_core.c ++++ b/drivers/iio/imu/adis16400_core.c +@@ -780,7 +780,7 @@ static struct adis16400_chip_info adis16400_chips[] = { + .flags = ADIS16400_HAS_PROD_ID | + ADIS16400_HAS_SERIAL_NUMBER | + ADIS16400_BURST_DIAG_STAT, +- .gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */ ++ .gyro_scale_micro = IIO_DEGREE_TO_RAD(40000), /* 0.04 deg/s */ + .accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */ + .temp_scale_nano = 73860000, /* 0.07386 C */ + .temp_offset = 31000000 / 73860, /* 31 C = 0x00 */ +diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c +index 989605dd6f78..b94bfd3f595b 100644 +--- a/drivers/iio/imu/adis16480.c ++++ b/drivers/iio/imu/adis16480.c +@@ -110,6 +110,10 @@ + struct adis16480_chip_info { + unsigned int num_channels; + const struct iio_chan_spec *channels; ++ unsigned int gyro_max_val; ++ unsigned int gyro_max_scale; ++ unsigned int accel_max_val; ++ unsigned int accel_max_scale; + }; + + struct adis16480 { +@@ -497,19 +501,21 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, + static int adis16480_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) + { ++ struct adis16480 *st = iio_priv(indio_dev); ++ + switch (info) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, 0, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ANGL_VEL: +- *val = 0; +- *val2 = IIO_DEGREE_TO_RAD(20000); /* 0.02 degree/sec */ +- return IIO_VAL_INT_PLUS_MICRO; ++ *val = st->chip_info->gyro_max_scale; ++ *val2 = st->chip_info->gyro_max_val; ++ return IIO_VAL_FRACTIONAL; + case IIO_ACCEL: +- *val = 0; +- *val2 = IIO_G_TO_M_S_2(800); /* 0.8 mg */ +- return IIO_VAL_INT_PLUS_MICRO; ++ *val = st->chip_info->accel_max_scale; ++ *val2 = st->chip_info->accel_max_val; ++ return IIO_VAL_FRACTIONAL; + case IIO_MAGN: + *val = 0; + *val2 = 100; /* 0.0001 gauss */ +@@ -674,18 +680,39 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { + [ADIS16375] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), ++ /* ++ * storing the value in rad/degree and the scale in degree ++ * gives us the result in rad and better precession than ++ * storing the scale directly in rad. ++ */ ++ .gyro_max_val = IIO_RAD_TO_DEGREE(22887), ++ .gyro_max_scale = 300, ++ .accel_max_val = IIO_M_S_2_TO_G(21973), ++ .accel_max_scale = 18, + }, + [ADIS16480] = { + .channels = adis16480_channels, + .num_channels = ARRAY_SIZE(adis16480_channels), ++ .gyro_max_val = IIO_RAD_TO_DEGREE(22500), ++ .gyro_max_scale = 450, ++ .accel_max_val = IIO_M_S_2_TO_G(12500), ++ .accel_max_scale = 5, + }, + [ADIS16485] = { + .channels = adis16485_channels, + .num_channels = ARRAY_SIZE(adis16485_channels), ++ .gyro_max_val = IIO_RAD_TO_DEGREE(22500), ++ .gyro_max_scale = 450, ++ .accel_max_val = IIO_M_S_2_TO_G(20000), ++ .accel_max_scale = 5, + }, + [ADIS16488] = { + .channels = adis16480_channels, + .num_channels = ARRAY_SIZE(adis16480_channels), ++ .gyro_max_val = IIO_RAD_TO_DEGREE(22500), ++ .gyro_max_scale = 450, ++ .accel_max_val = IIO_M_S_2_TO_G(22500), ++ .accel_max_scale = 18, + }, + }; + +diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c +index 6eee1b044c60..b3fda9ee4174 100644 +--- a/drivers/iio/industrialio-buffer.c ++++ b/drivers/iio/industrialio-buffer.c +@@ -151,7 +151,7 @@ unsigned int iio_buffer_poll(struct file *filp, + struct iio_buffer *rb = indio_dev->buffer; + + if (!indio_dev->info) +- return -ENODEV; ++ return 0; + + poll_wait(filp, &rb->pollq, wait); + if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) +diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c +index 894d8137c4cf..52d4fcb0de1d 100644 +--- a/drivers/iio/industrialio-event.c ++++ b/drivers/iio/industrialio-event.c +@@ -84,7 +84,7 @@ static unsigned int iio_event_poll(struct file *filep, + unsigned int events = 0; + + if (!indio_dev->info) +- return -ENODEV; ++ return events; + + poll_wait(filep, &ev_int->wait, wait); + +diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c +index 1fe93cfea7d3..9d0672b58c31 100644 +--- a/drivers/md/dm-cache-target.c ++++ b/drivers/md/dm-cache-target.c +@@ -1729,6 +1729,8 @@ static void remap_cell_to_origin_clear_discard(struct cache *cache, + remap_to_origin(cache, bio); + issue(cache, bio); + } ++ ++ free_prison_cell(cache, cell); + } + + static void remap_cell_to_cache_dirty(struct cache *cache, struct dm_bio_prison_cell *cell, +@@ -1763,6 +1765,8 @@ static void remap_cell_to_cache_dirty(struct cache *cache, struct dm_bio_prison_ + remap_to_cache(cache, bio, cblock); + issue(cache, bio); + } ++ ++ free_prison_cell(cache, cell); + } + + /*----------------------------------------------------------------*/ +diff --git a/drivers/md/dm-stats.c b/drivers/md/dm-stats.c +index 8a8b48fa901a..8289804ccd99 100644 +--- a/drivers/md/dm-stats.c ++++ b/drivers/md/dm-stats.c +@@ -457,12 +457,24 @@ static int dm_stats_list(struct dm_stats *stats, const char *program, + list_for_each_entry(s, &stats->list, list_entry) { + if (!program || !strcmp(program, s->program_id)) { + len = s->end - s->start; +- DMEMIT("%d: %llu+%llu %llu %s %s\n", s->id, ++ DMEMIT("%d: %llu+%llu %llu %s %s", s->id, + (unsigned long long)s->start, + (unsigned long long)len, + (unsigned long long)s->step, + s->program_id, + s->aux_data); ++ if (s->stat_flags & STAT_PRECISE_TIMESTAMPS) ++ DMEMIT(" precise_timestamps"); ++ if (s->n_histogram_entries) { ++ unsigned i; ++ DMEMIT(" histogram:"); ++ for (i = 0; i < s->n_histogram_entries; i++) { ++ if (i) ++ DMEMIT(","); ++ DMEMIT("%llu", s->histogram_boundaries[i]); ++ } ++ } ++ DMEMIT("\n"); + } + } + mutex_unlock(&stats->mutex); +diff --git a/drivers/of/address.c b/drivers/of/address.c +index 8bfda6ade2c0..384574c3987c 100644 +--- a/drivers/of/address.c ++++ b/drivers/of/address.c +@@ -845,10 +845,10 @@ struct device_node *of_find_matching_node_by_address(struct device_node *from, + struct resource res; + + while (dn) { +- if (of_address_to_resource(dn, 0, &res)) +- continue; +- if (res.start == base_address) ++ if (!of_address_to_resource(dn, 0, &res) && ++ res.start == base_address) + return dn; ++ + dn = of_find_matching_node(dn, matches); + } + +diff --git a/drivers/pci/access.c b/drivers/pci/access.c +index d9b64a175990..b965c12168b7 100644 +--- a/drivers/pci/access.c ++++ b/drivers/pci/access.c +@@ -439,6 +439,56 @@ static const struct pci_vpd_ops pci_vpd_pci22_ops = { + .release = pci_vpd_pci22_release, + }; + ++static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, ++ void *arg) ++{ ++ struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); ++ ssize_t ret; ++ ++ if (!tdev) ++ return -ENODEV; ++ ++ ret = pci_read_vpd(tdev, pos, count, arg); ++ pci_dev_put(tdev); ++ return ret; ++} ++ ++static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, ++ const void *arg) ++{ ++ struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); ++ ssize_t ret; ++ ++ if (!tdev) ++ return -ENODEV; ++ ++ ret = pci_write_vpd(tdev, pos, count, arg); ++ pci_dev_put(tdev); ++ return ret; ++} ++ ++static const struct pci_vpd_ops pci_vpd_f0_ops = { ++ .read = pci_vpd_f0_read, ++ .write = pci_vpd_f0_write, ++ .release = pci_vpd_pci22_release, ++}; ++ ++static int pci_vpd_f0_dev_check(struct pci_dev *dev) ++{ ++ struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); ++ int ret = 0; ++ ++ if (!tdev) ++ return -ENODEV; ++ if (!tdev->vpd || !tdev->multifunction || ++ dev->class != tdev->class || dev->vendor != tdev->vendor || ++ dev->device != tdev->device) ++ ret = -ENODEV; ++ ++ pci_dev_put(tdev); ++ return ret; ++} ++ + int pci_vpd_pci22_init(struct pci_dev *dev) + { + struct pci_vpd_pci22 *vpd; +@@ -447,12 +497,21 @@ int pci_vpd_pci22_init(struct pci_dev *dev) + cap = pci_find_capability(dev, PCI_CAP_ID_VPD); + if (!cap) + return -ENODEV; ++ if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) { ++ int ret = pci_vpd_f0_dev_check(dev); ++ ++ if (ret) ++ return ret; ++ } + vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); + if (!vpd) + return -ENOMEM; + + vpd->base.len = PCI_VPD_PCI22_SIZE; +- vpd->base.ops = &pci_vpd_pci22_ops; ++ if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) ++ vpd->base.ops = &pci_vpd_f0_ops; ++ else ++ vpd->base.ops = &pci_vpd_pci22_ops; + mutex_init(&vpd->lock); + vpd->cap = cap; + vpd->busy = false; +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index e9fd0e90fa3b..dbd13854f21e 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -1569,6 +1569,18 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB3 + + #endif + ++static void quirk_jmicron_async_suspend(struct pci_dev *dev) ++{ ++ if (dev->multifunction) { ++ device_disable_async_suspend(&dev->dev); ++ dev_info(&dev->dev, "async suspend disabled to avoid multi-function power-on ordering issue\n"); ++ } ++} ++DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE, 8, quirk_jmicron_async_suspend); ++DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_CLASS_STORAGE_SATA_AHCI, 0, quirk_jmicron_async_suspend); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_JMICRON, 0x2362, quirk_jmicron_async_suspend); ++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_JMICRON, 0x236f, quirk_jmicron_async_suspend); ++ + #ifdef CONFIG_X86_IO_APIC + static void quirk_alder_ioapic(struct pci_dev *pdev) + { +@@ -1894,6 +1906,15 @@ static void quirk_netmos(struct pci_dev *dev) + DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos); + ++static void quirk_f0_vpd_link(struct pci_dev *dev) ++{ ++ if (!dev->multifunction || !PCI_FUNC(dev->devfn)) ++ return; ++ dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; ++} ++DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, ++ PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); ++ + static void quirk_e100_interrupt(struct pci_dev *dev) + { + u16 command, pmcsr; +@@ -2829,12 +2850,15 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3c28, vtd_mask_spec_errors); + + static void fixup_ti816x_class(struct pci_dev *dev) + { ++ u32 class = dev->class; ++ + /* TI 816x devices do not have class code set when in PCIe boot mode */ +- dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n"); +- dev->class = PCI_CLASS_MULTIMEDIA_VIDEO; ++ dev->class = PCI_CLASS_MULTIMEDIA_VIDEO << 8; ++ dev_info(&dev->dev, "PCI class overridden (%#08x -> %#08x)\n", ++ class, dev->class); + } + DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_TI, 0xb800, +- PCI_CLASS_NOT_DEFINED, 0, fixup_ti816x_class); ++ PCI_CLASS_NOT_DEFINED, 0, fixup_ti816x_class); + + /* Some PCIe devices do not work reliably with the claimed maximum + * payload size supported. +diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c +index bd2b75c0d1d1..4fa7bcaf454e 100644 +--- a/drivers/regulator/pbias-regulator.c ++++ b/drivers/regulator/pbias-regulator.c +@@ -30,6 +30,7 @@ + struct pbias_reg_info { + u32 enable; + u32 enable_mask; ++ u32 disable_val; + u32 vmode; + unsigned int enable_time; + char *name; +@@ -62,6 +63,7 @@ static const struct pbias_reg_info pbias_mmc_omap2430 = { + .enable = BIT(1), + .enable_mask = BIT(1), + .vmode = BIT(0), ++ .disable_val = 0, + .enable_time = 100, + .name = "pbias_mmc_omap2430" + }; +@@ -77,6 +79,7 @@ static const struct pbias_reg_info pbias_sim_omap3 = { + static const struct pbias_reg_info pbias_mmc_omap4 = { + .enable = BIT(26) | BIT(22), + .enable_mask = BIT(26) | BIT(25) | BIT(22), ++ .disable_val = BIT(25), + .vmode = BIT(21), + .enable_time = 100, + .name = "pbias_mmc_omap4" +@@ -85,6 +88,7 @@ static const struct pbias_reg_info pbias_mmc_omap4 = { + static const struct pbias_reg_info pbias_mmc_omap5 = { + .enable = BIT(27) | BIT(26), + .enable_mask = BIT(27) | BIT(25) | BIT(26), ++ .disable_val = BIT(25), + .vmode = BIT(21), + .enable_time = 100, + .name = "pbias_mmc_omap5" +@@ -159,6 +163,7 @@ static int pbias_regulator_probe(struct platform_device *pdev) + drvdata[data_idx].desc.enable_reg = res->start; + drvdata[data_idx].desc.enable_mask = info->enable_mask; + drvdata[data_idx].desc.enable_val = info->enable; ++ drvdata[data_idx].desc.disable_val = info->disable_val; + + cfg.init_data = pbias_matches[idx].init_data; + cfg.driver_data = &drvdata[data_idx]; +diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c +index 75d0457a77b7..fa7036c4daf9 100644 +--- a/drivers/soc/tegra/pmc.c ++++ b/drivers/soc/tegra/pmc.c +@@ -736,12 +736,12 @@ void tegra_pmc_init_tsense_reset(struct tegra_pmc *pmc) + u32 value, checksum; + + if (!pmc->soc->has_tsense_reset) +- goto out; ++ return; + + np = of_find_node_by_name(pmc->dev->of_node, "i2c-thermtrip"); + if (!np) { + dev_warn(dev, "i2c-thermtrip node not found, %s.\n", disabled); +- goto out; ++ return; + } + + if (of_property_read_u32(np, "nvidia,i2c-controller-id", &ctrl_id)) { +diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c +index 59705ab23577..c9357bb393d3 100644 +--- a/drivers/spi/spi-bcm2835.c ++++ b/drivers/spi/spi-bcm2835.c +@@ -553,13 +553,11 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, + spi_used_hz = cdiv ? (clk_hz / cdiv) : (clk_hz / 65536); + bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv); + +- /* handle all the modes */ ++ /* handle all the 3-wire mode */ + if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf)) + cs |= BCM2835_SPI_CS_REN; +- if (spi->mode & SPI_CPOL) +- cs |= BCM2835_SPI_CS_CPOL; +- if (spi->mode & SPI_CPHA) +- cs |= BCM2835_SPI_CS_CPHA; ++ else ++ cs &= ~BCM2835_SPI_CS_REN; + + /* for gpio_cs set dummy CS so that no HW-CS get changed + * we can not run this in bcm2835_spi_set_cs, as it does +@@ -592,6 +590,25 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, + return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); + } + ++static int bcm2835_spi_prepare_message(struct spi_master *master, ++ struct spi_message *msg) ++{ ++ struct spi_device *spi = msg->spi; ++ struct bcm2835_spi *bs = spi_master_get_devdata(master); ++ u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); ++ ++ cs &= ~(BCM2835_SPI_CS_CPOL | BCM2835_SPI_CS_CPHA); ++ ++ if (spi->mode & SPI_CPOL) ++ cs |= BCM2835_SPI_CS_CPOL; ++ if (spi->mode & SPI_CPHA) ++ cs |= BCM2835_SPI_CS_CPHA; ++ ++ bcm2835_wr(bs, BCM2835_SPI_CS, cs); ++ ++ return 0; ++} ++ + static void bcm2835_spi_handle_err(struct spi_master *master, + struct spi_message *msg) + { +@@ -739,6 +756,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) + master->set_cs = bcm2835_spi_set_cs; + master->transfer_one = bcm2835_spi_transfer_one; + master->handle_err = bcm2835_spi_handle_err; ++ master->prepare_message = bcm2835_spi_prepare_message; + master->dev.of_node = pdev->dev.of_node; + + bs = spi_master_get_devdata(master); +diff --git a/drivers/spi/spi-bitbang-txrx.h b/drivers/spi/spi-bitbang-txrx.h +index 06b34e5bcfa3..47bb9b898dfd 100644 +--- a/drivers/spi/spi-bitbang-txrx.h ++++ b/drivers/spi/spi-bitbang-txrx.h +@@ -49,7 +49,7 @@ bitbang_txrx_be_cpha0(struct spi_device *spi, + { + /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ + +- bool oldbit = !(word & 1); ++ u32 oldbit = (!(word & (1<<(bits-1)))) << 31; + /* clock starts at inactive polarity */ + for (word <<= (32 - bits); likely(bits); bits--) { + +@@ -81,7 +81,7 @@ bitbang_txrx_be_cpha1(struct spi_device *spi, + { + /* if (cpol == 0) this is SPI_MODE_1; else this is SPI_MODE_3 */ + +- bool oldbit = !(word & (1 << 31)); ++ u32 oldbit = (!(word & (1<<(bits-1)))) << 31; + /* clock starts at inactive polarity */ + for (word <<= (32 - bits); likely(bits); bits--) { + +diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c +index eb03e1215195..7edede6e024b 100644 +--- a/drivers/spi/spi-dw-mmio.c ++++ b/drivers/spi/spi-dw-mmio.c +@@ -74,6 +74,9 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) + + dws->max_freq = clk_get_rate(dwsmmio->clk); + ++ of_property_read_u32(pdev->dev.of_node, "reg-io-width", ++ &dws->reg_io_width); ++ + num_cs = 4; + + if (pdev->dev.of_node) +diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c +index 8d67d03c71eb..4fbfcdc5cb24 100644 +--- a/drivers/spi/spi-dw.c ++++ b/drivers/spi/spi-dw.c +@@ -194,7 +194,7 @@ static void dw_writer(struct dw_spi *dws) + else + txw = *(u16 *)(dws->tx); + } +- dw_writel(dws, DW_SPI_DR, txw); ++ dw_write_io_reg(dws, DW_SPI_DR, txw); + dws->tx += dws->n_bytes; + } + } +@@ -205,7 +205,7 @@ static void dw_reader(struct dw_spi *dws) + u16 rxw; + + while (max--) { +- rxw = dw_readl(dws, DW_SPI_DR); ++ rxw = dw_read_io_reg(dws, DW_SPI_DR); + /* Care rx only if the transfer's original "rx" is not null */ + if (dws->rx_end - dws->len) { + if (dws->n_bytes == 1) +diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h +index 6c91391c1a4f..b75ed327d5a2 100644 +--- a/drivers/spi/spi-dw.h ++++ b/drivers/spi/spi-dw.h +@@ -109,6 +109,7 @@ struct dw_spi { + u32 fifo_len; /* depth of the FIFO buffer */ + u32 max_freq; /* max bus freq supported */ + ++ u32 reg_io_width; /* DR I/O width in bytes */ + u16 bus_num; + u16 num_cs; /* supported slave numbers */ + +@@ -145,11 +146,45 @@ static inline u32 dw_readl(struct dw_spi *dws, u32 offset) + return __raw_readl(dws->regs + offset); + } + ++static inline u16 dw_readw(struct dw_spi *dws, u32 offset) ++{ ++ return __raw_readw(dws->regs + offset); ++} ++ + static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val) + { + __raw_writel(val, dws->regs + offset); + } + ++static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val) ++{ ++ __raw_writew(val, dws->regs + offset); ++} ++ ++static inline u32 dw_read_io_reg(struct dw_spi *dws, u32 offset) ++{ ++ switch (dws->reg_io_width) { ++ case 2: ++ return dw_readw(dws, offset); ++ case 4: ++ default: ++ return dw_readl(dws, offset); ++ } ++} ++ ++static inline void dw_write_io_reg(struct dw_spi *dws, u32 offset, u32 val) ++{ ++ switch (dws->reg_io_width) { ++ case 2: ++ dw_writew(dws, offset, val); ++ break; ++ case 4: ++ default: ++ dw_writel(dws, offset, val); ++ break; ++ } ++} ++ + static inline void spi_enable_chip(struct dw_spi *dws, int enable) + { + dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0)); +diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c +index acce90ac7371..bb916c8d40db 100644 +--- a/drivers/spi/spi-img-spfi.c ++++ b/drivers/spi/spi-img-spfi.c +@@ -105,6 +105,10 @@ struct img_spfi { + bool rx_dma_busy; + }; + ++struct img_spfi_device_data { ++ bool gpio_requested; ++}; ++ + static inline u32 spfi_readl(struct img_spfi *spfi, u32 reg) + { + return readl(spfi->regs + reg); +@@ -267,15 +271,15 @@ static int img_spfi_start_pio(struct spi_master *master, + cpu_relax(); + } + +- ret = spfi_wait_all_done(spfi); +- if (ret < 0) +- return ret; +- + if (rx_bytes > 0 || tx_bytes > 0) { + dev_err(spfi->dev, "PIO transfer timed out\n"); + return -ETIMEDOUT; + } + ++ ret = spfi_wait_all_done(spfi); ++ if (ret < 0) ++ return ret; ++ + return 0; + } + +@@ -440,21 +444,50 @@ static int img_spfi_unprepare(struct spi_master *master, + + static int img_spfi_setup(struct spi_device *spi) + { +- int ret; +- +- ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ? +- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, +- dev_name(&spi->dev)); +- if (ret) +- dev_err(&spi->dev, "can't request chipselect gpio %d\n", ++ int ret = -EINVAL; ++ struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi); ++ ++ if (!spfi_data) { ++ spfi_data = kzalloc(sizeof(*spfi_data), GFP_KERNEL); ++ if (!spfi_data) ++ return -ENOMEM; ++ spfi_data->gpio_requested = false; ++ spi_set_ctldata(spi, spfi_data); ++ } ++ if (!spfi_data->gpio_requested) { ++ ret = gpio_request_one(spi->cs_gpio, ++ (spi->mode & SPI_CS_HIGH) ? ++ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, ++ dev_name(&spi->dev)); ++ if (ret) ++ dev_err(&spi->dev, "can't request chipselect gpio %d\n", + spi->cs_gpio); +- ++ else ++ spfi_data->gpio_requested = true; ++ } else { ++ if (gpio_is_valid(spi->cs_gpio)) { ++ int mode = ((spi->mode & SPI_CS_HIGH) ? ++ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH); ++ ++ ret = gpio_direction_output(spi->cs_gpio, mode); ++ if (ret) ++ dev_err(&spi->dev, "chipselect gpio %d setup failed (%d)\n", ++ spi->cs_gpio, ret); ++ } ++ } + return ret; + } + + static void img_spfi_cleanup(struct spi_device *spi) + { +- gpio_free(spi->cs_gpio); ++ struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi); ++ ++ if (spfi_data) { ++ if (spfi_data->gpio_requested) ++ gpio_free(spi->cs_gpio); ++ kfree(spfi_data); ++ spi_set_ctldata(spi, NULL); ++ } + } + + static void img_spfi_config(struct spi_master *master, struct spi_device *spi, +diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c +index 58673841286c..3d09e0b69b73 100644 +--- a/drivers/spi/spi-omap2-mcspi.c ++++ b/drivers/spi/spi-omap2-mcspi.c +@@ -245,6 +245,7 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) + + static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) + { ++ struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); + u32 l; + + /* The controller handles the inverted chip selects +@@ -255,6 +256,12 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) + enable = !enable; + + if (spi->controller_state) { ++ int err = pm_runtime_get_sync(mcspi->dev); ++ if (err < 0) { ++ dev_err(mcspi->dev, "failed to get sync: %d\n", err); ++ return; ++ } ++ + l = mcspi_cached_chconf0(spi); + + if (enable) +@@ -263,6 +270,9 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) + l |= OMAP2_MCSPI_CHCONF_FORCE; + + mcspi_write_chconf0(spi, l); ++ ++ pm_runtime_mark_last_busy(mcspi->dev); ++ pm_runtime_put_autosuspend(mcspi->dev); + } + } + +diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c +index 8cad107a5b3f..a87cfd4ba17b 100644 +--- a/drivers/spi/spi-orion.c ++++ b/drivers/spi/spi-orion.c +@@ -41,6 +41,11 @@ + #define ORION_SPI_DATA_OUT_REG 0x08 + #define ORION_SPI_DATA_IN_REG 0x0c + #define ORION_SPI_INT_CAUSE_REG 0x10 ++#define ORION_SPI_TIMING_PARAMS_REG 0x18 ++ ++#define ORION_SPI_TMISO_SAMPLE_MASK (0x3 << 6) ++#define ORION_SPI_TMISO_SAMPLE_1 (1 << 6) ++#define ORION_SPI_TMISO_SAMPLE_2 (2 << 6) + + #define ORION_SPI_MODE_CPOL (1 << 11) + #define ORION_SPI_MODE_CPHA (1 << 12) +@@ -70,6 +75,7 @@ struct orion_spi_dev { + unsigned int min_divisor; + unsigned int max_divisor; + u32 prescale_mask; ++ bool is_errata_50mhz_ac; + }; + + struct orion_spi { +@@ -195,6 +201,41 @@ orion_spi_mode_set(struct spi_device *spi) + writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); + } + ++static void ++orion_spi_50mhz_ac_timing_erratum(struct spi_device *spi, unsigned int speed) ++{ ++ u32 reg; ++ struct orion_spi *orion_spi; ++ ++ orion_spi = spi_master_get_devdata(spi->master); ++ ++ /* ++ * Erratum description: (Erratum NO. FE-9144572) The device ++ * SPI interface supports frequencies of up to 50 MHz. ++ * However, due to this erratum, when the device core clock is ++ * 250 MHz and the SPI interfaces is configured for 50MHz SPI ++ * clock and CPOL=CPHA=1 there might occur data corruption on ++ * reads from the SPI device. ++ * Erratum Workaround: ++ * Work in one of the following configurations: ++ * 1. Set CPOL=CPHA=0 in "SPI Interface Configuration ++ * Register". ++ * 2. Set TMISO_SAMPLE value to 0x2 in "SPI Timing Parameters 1 ++ * Register" before setting the interface. ++ */ ++ reg = readl(spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG)); ++ reg &= ~ORION_SPI_TMISO_SAMPLE_MASK; ++ ++ if (clk_get_rate(orion_spi->clk) == 250000000 && ++ speed == 50000000 && spi->mode & SPI_CPOL && ++ spi->mode & SPI_CPHA) ++ reg |= ORION_SPI_TMISO_SAMPLE_2; ++ else ++ reg |= ORION_SPI_TMISO_SAMPLE_1; /* This is the default value */ ++ ++ writel(reg, spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG)); ++} ++ + /* + * called only when no transfer is active on the bus + */ +@@ -216,6 +257,9 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) + + orion_spi_mode_set(spi); + ++ if (orion_spi->devdata->is_errata_50mhz_ac) ++ orion_spi_50mhz_ac_timing_erratum(spi, speed); ++ + rc = orion_spi_baudrate_set(spi, speed); + if (rc) + return rc; +@@ -413,6 +457,14 @@ static const struct orion_spi_dev armada_375_spi_dev_data = { + .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, + }; + ++static const struct orion_spi_dev armada_380_spi_dev_data = { ++ .typ = ARMADA_SPI, ++ .max_hz = 50000000, ++ .max_divisor = 1920, ++ .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, ++ .is_errata_50mhz_ac = true, ++}; ++ + static const struct of_device_id orion_spi_of_match_table[] = { + { + .compatible = "marvell,orion-spi", +@@ -428,7 +480,7 @@ static const struct of_device_id orion_spi_of_match_table[] = { + }, + { + .compatible = "marvell,armada-380-spi", +- .data = &armada_xp_spi_dev_data, ++ .data = &armada_380_spi_dev_data, + }, + { + .compatible = "marvell,armada-390-spi", +diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c +index d3370a612d84..a7629f8edfca 100644 +--- a/drivers/spi/spi-sh-msiof.c ++++ b/drivers/spi/spi-sh-msiof.c +@@ -48,8 +48,8 @@ struct sh_msiof_spi_priv { + const struct sh_msiof_chipdata *chipdata; + struct sh_msiof_spi_info *info; + struct completion done; +- int tx_fifo_size; +- int rx_fifo_size; ++ unsigned int tx_fifo_size; ++ unsigned int rx_fifo_size; + void *tx_dma_page; + void *rx_dma_page; + dma_addr_t tx_dma_addr; +@@ -95,8 +95,6 @@ struct sh_msiof_spi_priv { + #define MDR2_WDLEN1(i) (((i) - 1) << 16) /* Word Count (1-64/256 (SH, A1))) */ + #define MDR2_GRPMASK1 0x00000001 /* Group Output Mask 1 (SH, A1) */ + +-#define MAX_WDLEN 256U +- + /* TSCR and RSCR */ + #define SCR_BRPS_MASK 0x1f00 /* Prescaler Setting (1-32) */ + #define SCR_BRPS(i) (((i) - 1) << 8) +@@ -850,7 +848,12 @@ static int sh_msiof_transfer_one(struct spi_master *master, + * DMA supports 32-bit words only, hence pack 8-bit and 16-bit + * words, with byte resp. word swapping. + */ +- unsigned int l = min(len, MAX_WDLEN * 4); ++ unsigned int l = 0; ++ ++ if (tx_buf) ++ l = min(len, p->tx_fifo_size * 4); ++ if (rx_buf) ++ l = min(len, p->rx_fifo_size * 4); + + if (bits <= 8) { + if (l & 3) +@@ -963,7 +966,7 @@ static const struct sh_msiof_chipdata sh_data = { + + static const struct sh_msiof_chipdata r8a779x_data = { + .tx_fifo_size = 64, +- .rx_fifo_size = 256, ++ .rx_fifo_size = 64, + .master_flags = SPI_MASTER_MUST_TX, + }; + +diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c +index 133f53a9c1d4..a339c1e9997a 100644 +--- a/drivers/spi/spi-xilinx.c ++++ b/drivers/spi/spi-xilinx.c +@@ -249,19 +249,23 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) + xspi->tx_ptr = t->tx_buf; + xspi->rx_ptr = t->rx_buf; + remaining_words = t->len / xspi->bytes_per_word; +- reinit_completion(&xspi->done); + + if (xspi->irq >= 0 && remaining_words > xspi->buffer_size) { ++ u32 isr; + use_irq = true; +- xspi->write_fn(XSPI_INTR_TX_EMPTY, +- xspi->regs + XIPIF_V123B_IISR_OFFSET); +- /* Enable the global IPIF interrupt */ +- xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, +- xspi->regs + XIPIF_V123B_DGIER_OFFSET); + /* Inhibit irq to avoid spurious irqs on tx_empty*/ + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); + xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, + xspi->regs + XSPI_CR_OFFSET); ++ /* ACK old irqs (if any) */ ++ isr = xspi->read_fn(xspi->regs + XIPIF_V123B_IISR_OFFSET); ++ if (isr) ++ xspi->write_fn(isr, ++ xspi->regs + XIPIF_V123B_IISR_OFFSET); ++ /* Enable the global IPIF interrupt */ ++ xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, ++ xspi->regs + XIPIF_V123B_DGIER_OFFSET); ++ reinit_completion(&xspi->done); + } + + while (remaining_words) { +@@ -302,8 +306,10 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) + remaining_words -= n_words; + } + +- if (use_irq) ++ if (use_irq) { + xspi->write_fn(0, xspi->regs + XIPIF_V123B_DGIER_OFFSET); ++ xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); ++ } + + return t->len; + } +diff --git a/drivers/staging/comedi/drivers/adl_pci7x3x.c b/drivers/staging/comedi/drivers/adl_pci7x3x.c +index 934af3ff7897..b0fc027cf485 100644 +--- a/drivers/staging/comedi/drivers/adl_pci7x3x.c ++++ b/drivers/staging/comedi/drivers/adl_pci7x3x.c +@@ -120,8 +120,20 @@ static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev, + { + unsigned long reg = (unsigned long)s->private; + +- if (comedi_dio_update_state(s, data)) +- outl(s->state, dev->iobase + reg); ++ if (comedi_dio_update_state(s, data)) { ++ unsigned int val = s->state; ++ ++ if (s->n_chan == 16) { ++ /* ++ * It seems the PCI-7230 needs the 16-bit DO state ++ * to be shifted left by 16 bits before being written ++ * to the 32-bit register. Set the value in both ++ * halves of the register to be sure. ++ */ ++ val |= val << 16; ++ } ++ outl(val, dev->iobase + reg); ++ } + + data[1] = s->state; + +diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c +index eaa9add491df..dc0b25a54088 100644 +--- a/drivers/staging/comedi/drivers/usbduxsigma.c ++++ b/drivers/staging/comedi/drivers/usbduxsigma.c +@@ -550,27 +550,6 @@ static int usbduxsigma_ai_cmdtest(struct comedi_device *dev, + if (err) + return 3; + +- /* Step 4: fix up any arguments */ +- +- if (high_speed) { +- /* +- * every 2 channels get a time window of 125us. Thus, if we +- * sample all 16 channels we need 1ms. If we sample only one +- * channel we need only 125us +- */ +- devpriv->ai_interval = interval; +- devpriv->ai_timer = cmd->scan_begin_arg / (125000 * interval); +- } else { +- /* interval always 1ms */ +- devpriv->ai_interval = 1; +- devpriv->ai_timer = cmd->scan_begin_arg / 1000000; +- } +- if (devpriv->ai_timer < 1) +- err |= -EINVAL; +- +- if (err) +- return 4; +- + return 0; + } + +@@ -668,6 +647,22 @@ static int usbduxsigma_ai_cmd(struct comedi_device *dev, + + down(&devpriv->sem); + ++ if (devpriv->high_speed) { ++ /* ++ * every 2 channels get a time window of 125us. Thus, if we ++ * sample all 16 channels we need 1ms. If we sample only one ++ * channel we need only 125us ++ */ ++ unsigned int interval = usbduxsigma_chans_to_interval(len); ++ ++ devpriv->ai_interval = interval; ++ devpriv->ai_timer = cmd->scan_begin_arg / (125000 * interval); ++ } else { ++ /* interval always 1ms */ ++ devpriv->ai_interval = 1; ++ devpriv->ai_timer = cmd->scan_begin_arg / 1000000; ++ } ++ + for (i = 0; i < len; i++) { + unsigned int chan = CR_CHAN(cmd->chanlist[i]); + +@@ -917,25 +912,6 @@ static int usbduxsigma_ao_cmdtest(struct comedi_device *dev, + if (err) + return 3; + +- /* Step 4: fix up any arguments */ +- +- /* we count in timer steps */ +- if (high_speed) { +- /* timing of the conversion itself: every 125 us */ +- devpriv->ao_timer = cmd->convert_arg / 125000; +- } else { +- /* +- * timing of the scan: every 1ms +- * we get all channels at once +- */ +- devpriv->ao_timer = cmd->scan_begin_arg / 1000000; +- } +- if (devpriv->ao_timer < 1) +- err |= -EINVAL; +- +- if (err) +- return 4; +- + return 0; + } + +@@ -948,6 +924,20 @@ static int usbduxsigma_ao_cmd(struct comedi_device *dev, + + down(&devpriv->sem); + ++ if (cmd->convert_src == TRIG_TIMER) { ++ /* ++ * timing of the conversion itself: every 125 us ++ * at high speed (not used yet) ++ */ ++ devpriv->ao_timer = cmd->convert_arg / 125000; ++ } else { ++ /* ++ * timing of the scan: every 1ms ++ * we get all channels at once ++ */ ++ devpriv->ao_timer = cmd->scan_begin_arg / 1000000; ++ } ++ + devpriv->ao_counter = devpriv->ao_timer; + + if (cmd->start_src == TRIG_NOW) { +diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +index c6cdb43b864c..476808261fa8 100644 +--- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c ++++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c +@@ -1826,8 +1826,8 @@ void rtl8192_hard_data_xmit(struct sk_buff *skb, struct net_device *dev, + return; + } + +- if (queue_index != TXCMD_QUEUE) +- netdev_warn(dev, "%s(): queue index != TXCMD_QUEUE\n", ++ if (queue_index == TXCMD_QUEUE) ++ netdev_warn(dev, "%s(): queue index == TXCMD_QUEUE\n", + __func__); + + memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); +diff --git a/drivers/staging/unisys/visorbus/visorchipset.c b/drivers/staging/unisys/visorbus/visorchipset.c +index bb8087e70127..44269d58eb51 100644 +--- a/drivers/staging/unisys/visorbus/visorchipset.c ++++ b/drivers/staging/unisys/visorbus/visorchipset.c +@@ -2381,6 +2381,9 @@ static struct acpi_driver unisys_acpi_driver = { + .remove = visorchipset_exit, + }, + }; ++ ++MODULE_DEVICE_TABLE(acpi, unisys_device_ids); ++ + static __init uint32_t visorutil_spar_detect(void) + { + unsigned int eax, ebx, ecx, edx; +diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c +index d75a66c72750..b470df122642 100644 +--- a/drivers/tty/serial/8250/8250_omap.c ++++ b/drivers/tty/serial/8250/8250_omap.c +@@ -100,6 +100,7 @@ struct omap8250_priv { + struct work_struct qos_work; + struct uart_8250_dma omap8250_dma; + spinlock_t rx_dma_lock; ++ bool rx_dma_broken; + }; + + static u32 uart_read(struct uart_8250_port *up, u32 reg) +@@ -754,6 +755,7 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) + struct omap8250_priv *priv = p->port.private_data; + struct uart_8250_dma *dma = p->dma; + unsigned long flags; ++ int ret; + + spin_lock_irqsave(&priv->rx_dma_lock, flags); + +@@ -762,7 +764,9 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) + return; + } + +- dmaengine_pause(dma->rxchan); ++ ret = dmaengine_pause(dma->rxchan); ++ if (WARN_ON_ONCE(ret)) ++ priv->rx_dma_broken = true; + + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); + +@@ -806,6 +810,9 @@ static int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) + break; + } + ++ if (priv->rx_dma_broken) ++ return -EINVAL; ++ + spin_lock_irqsave(&priv->rx_dma_lock, flags); + + if (dma->rx_running) +@@ -1180,6 +1187,11 @@ static int omap8250_probe(struct platform_device *pdev) + + if (of_machine_is_compatible("ti,am33xx")) + priv->habit |= OMAP_DMA_TX_KICK; ++ /* ++ * pause is currently not supported atleast on omap-sdma ++ * and edma on most earlier kernels. ++ */ ++ priv->rx_dma_broken = true; + } + } + #endif +diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c +index e55f18b93fe7..46ddce479f26 100644 +--- a/drivers/tty/serial/8250/8250_pci.c ++++ b/drivers/tty/serial/8250/8250_pci.c +@@ -2017,6 +2017,12 @@ pci_wch_ch38x_setup(struct serial_private *priv, + #define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 + #define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 + ++#define PCI_VENDOR_ID_PERICOM 0x12D8 ++#define PCI_DEVICE_ID_PERICOM_PI7C9X7951 0x7951 ++#define PCI_DEVICE_ID_PERICOM_PI7C9X7952 0x7952 ++#define PCI_DEVICE_ID_PERICOM_PI7C9X7954 0x7954 ++#define PCI_DEVICE_ID_PERICOM_PI7C9X7958 0x7958 ++ + /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ + #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 + #define PCI_SUBDEVICE_ID_UNKNOWN_0x1588 0x1588 +@@ -2331,27 +2337,12 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { + * Pericom + */ + { +- .vendor = 0x12d8, +- .device = 0x7952, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- .setup = pci_pericom_setup, +- }, +- { +- .vendor = 0x12d8, +- .device = 0x7954, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- .setup = pci_pericom_setup, +- }, +- { +- .vendor = 0x12d8, +- .device = 0x7958, +- .subvendor = PCI_ANY_ID, +- .subdevice = PCI_ANY_ID, +- .setup = pci_pericom_setup, ++ .vendor = PCI_VENDOR_ID_PERICOM, ++ .device = PCI_ANY_ID, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .setup = pci_pericom_setup, + }, +- + /* + * PLX + */ +@@ -3056,6 +3047,10 @@ enum pci_board_num_t { + pbn_fintek_8, + pbn_fintek_12, + pbn_wch384_4, ++ pbn_pericom_PI7C9X7951, ++ pbn_pericom_PI7C9X7952, ++ pbn_pericom_PI7C9X7954, ++ pbn_pericom_PI7C9X7958, + }; + + /* +@@ -3881,7 +3876,6 @@ static struct pciserial_board pci_boards[] = { + .base_baud = 115200, + .first_offset = 0x40, + }, +- + [pbn_wch384_4] = { + .flags = FL_BASE0, + .num_ports = 4, +@@ -3889,6 +3883,33 @@ static struct pciserial_board pci_boards[] = { + .uart_offset = 8, + .first_offset = 0xC0, + }, ++ /* ++ * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART ++ */ ++ [pbn_pericom_PI7C9X7951] = { ++ .flags = FL_BASE0, ++ .num_ports = 1, ++ .base_baud = 921600, ++ .uart_offset = 0x8, ++ }, ++ [pbn_pericom_PI7C9X7952] = { ++ .flags = FL_BASE0, ++ .num_ports = 2, ++ .base_baud = 921600, ++ .uart_offset = 0x8, ++ }, ++ [pbn_pericom_PI7C9X7954] = { ++ .flags = FL_BASE0, ++ .num_ports = 4, ++ .base_baud = 921600, ++ .uart_offset = 0x8, ++ }, ++ [pbn_pericom_PI7C9X7958] = { ++ .flags = FL_BASE0, ++ .num_ports = 8, ++ .base_baud = 921600, ++ .uart_offset = 0x8, ++ }, + }; + + static const struct pci_device_id blacklist[] = { +@@ -5154,6 +5175,25 @@ static struct pci_device_id serial_pci_tbl[] = { + 0, + 0, pbn_exar_XR17V8358 }, + /* ++ * Pericom PI7C9X795[1248] Uno/Dual/Quad/Octal UART ++ */ ++ { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7951, ++ PCI_ANY_ID, PCI_ANY_ID, ++ 0, ++ 0, pbn_pericom_PI7C9X7951 }, ++ { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7952, ++ PCI_ANY_ID, PCI_ANY_ID, ++ 0, ++ 0, pbn_pericom_PI7C9X7952 }, ++ { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7954, ++ PCI_ANY_ID, PCI_ANY_ID, ++ 0, ++ 0, pbn_pericom_PI7C9X7954 }, ++ { PCI_VENDOR_ID_PERICOM, PCI_DEVICE_ID_PERICOM_PI7C9X7958, ++ PCI_ANY_ID, PCI_ANY_ID, ++ 0, ++ 0, pbn_pericom_PI7C9X7958 }, ++ /* + * Topic TP560 Data/Fax/Voice 56k modem (reported by Evan Clarke) + */ + { PCI_VENDOR_ID_TOPIC, PCI_DEVICE_ID_TOPIC_TP560, +diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c +index 50a09cd76d50..658b392d1170 100644 +--- a/drivers/tty/serial/8250/8250_pnp.c ++++ b/drivers/tty/serial/8250/8250_pnp.c +@@ -41,6 +41,12 @@ static const struct pnp_device_id pnp_dev_table[] = { + { "AEI1240", 0 }, + /* Rockwell 56K ACF II Fax+Data+Voice Modem */ + { "AKY1021", 0 /*SPCI_FL_NO_SHIRQ*/ }, ++ /* ++ * ALi Fast Infrared Controller ++ * Native driver (ali-ircc) is broken so at least ++ * it can be used with irtty-sir. ++ */ ++ { "ALI5123", 0 }, + /* AZT3005 PnP SOUND DEVICE */ + { "AZT4001", 0 }, + /* Best Data Products Inc. Smart One 336F PnP Modem */ +@@ -364,6 +370,11 @@ static const struct pnp_device_id pnp_dev_table[] = { + /* Winbond CIR port, should not be probed. We should keep track + of it to prevent the legacy serial driver from probing it */ + { "WEC1022", CIR_PORT }, ++ /* ++ * SMSC IrCC SIR/FIR port, should not be probed by serial driver ++ * as well so its own driver can bind to it. ++ */ ++ { "SMCF010", CIR_PORT }, + { "", 0 } + }; + +diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c +index 7d79425c2b09..d11621e2cf1d 100644 +--- a/drivers/tty/serial/8250/8250_uniphier.c ++++ b/drivers/tty/serial/8250/8250_uniphier.c +@@ -218,6 +218,7 @@ static int uniphier_uart_probe(struct platform_device *pdev) + ret = serial8250_register_8250_port(&up); + if (ret < 0) { + dev_err(dev, "failed to register 8250 port\n"); ++ clk_disable_unprepare(priv->clk); + return ret; + } + +diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c +index 35c55505b3eb..5a41b8fbb10a 100644 +--- a/drivers/tty/serial/men_z135_uart.c ++++ b/drivers/tty/serial/men_z135_uart.c +@@ -392,7 +392,6 @@ static irqreturn_t men_z135_intr(int irq, void *data) + struct men_z135_port *uart = (struct men_z135_port *)data; + struct uart_port *port = &uart->port; + bool handled = false; +- unsigned long flags; + int irq_id; + + uart->stat_reg = ioread32(port->membase + MEN_Z135_STAT_REG); +@@ -401,7 +400,7 @@ static irqreturn_t men_z135_intr(int irq, void *data) + if (!irq_id) + goto out; + +- spin_lock_irqsave(&port->lock, flags); ++ spin_lock(&port->lock); + /* It's save to write to IIR[7:6] RXC[9:8] */ + iowrite8(irq_id, port->membase + MEN_Z135_STAT_REG); + +@@ -427,7 +426,7 @@ static irqreturn_t men_z135_intr(int irq, void *data) + handled = true; + } + +- spin_unlock_irqrestore(&port->lock, flags); ++ spin_unlock(&port->lock); + out: + return IRQ_RETVAL(handled); + } +@@ -717,7 +716,7 @@ static void men_z135_set_termios(struct uart_port *port, + + baud = uart_get_baud_rate(port, termios, old, 0, uart_freq / 16); + +- spin_lock(&port->lock); ++ spin_lock_irq(&port->lock); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + +@@ -725,7 +724,7 @@ static void men_z135_set_termios(struct uart_port *port, + iowrite32(bd_reg, port->membase + MEN_Z135_BAUD_REG); + + uart_update_timeout(port, termios->c_cflag, baud); +- spin_unlock(&port->lock); ++ spin_unlock_irq(&port->lock); + } + + static const char *men_z135_type(struct uart_port *port) +diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c +index 67d0c213b1c7..5916311eecb1 100644 +--- a/drivers/tty/serial/samsung.c ++++ b/drivers/tty/serial/samsung.c +@@ -295,15 +295,6 @@ static int s3c24xx_serial_start_tx_dma(struct s3c24xx_uart_port *ourport, + if (ourport->tx_mode != S3C24XX_TX_DMA) + enable_tx_dma(ourport); + +- while (xmit->tail & (dma_get_cache_alignment() - 1)) { +- if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) +- return 0; +- wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); +- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); +- port->icount.tx++; +- count--; +- } +- + dma->tx_size = count & ~(dma_get_cache_alignment() - 1); + dma->tx_transfer_addr = dma->tx_addr + xmit->tail; + +@@ -342,7 +333,9 @@ static void s3c24xx_serial_start_next_tx(struct s3c24xx_uart_port *ourport) + return; + } + +- if (!ourport->dma || !ourport->dma->tx_chan || count < port->fifosize) ++ if (!ourport->dma || !ourport->dma->tx_chan || ++ count < ourport->min_dma_size || ++ xmit->tail & (dma_get_cache_alignment() - 1)) + s3c24xx_serial_start_tx_pio(ourport); + else + s3c24xx_serial_start_tx_dma(ourport, count); +@@ -736,15 +729,20 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) + struct uart_port *port = &ourport->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; +- int count; ++ int count, dma_count = 0; + + spin_lock_irqsave(&port->lock, flags); + + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + +- if (ourport->dma && ourport->dma->tx_chan && count >= port->fifosize) { +- s3c24xx_serial_start_tx_dma(ourport, count); +- goto out; ++ if (ourport->dma && ourport->dma->tx_chan && ++ count >= ourport->min_dma_size) { ++ int align = dma_get_cache_alignment() - ++ (xmit->tail & (dma_get_cache_alignment() - 1)); ++ if (count-align >= ourport->min_dma_size) { ++ dma_count = count-align; ++ count = align; ++ } + } + + if (port->x_char) { +@@ -765,14 +763,24 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) + + /* try and drain the buffer... */ + +- count = port->fifosize; +- while (!uart_circ_empty(xmit) && count-- > 0) { ++ if (count > port->fifosize) { ++ count = port->fifosize; ++ dma_count = 0; ++ } ++ ++ while (!uart_circ_empty(xmit) && count > 0) { + if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) + break; + + wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; ++ count--; ++ } ++ ++ if (!count && dma_count) { ++ s3c24xx_serial_start_tx_dma(ourport, dma_count); ++ goto out; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { +@@ -1838,6 +1846,13 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) + else if (ourport->info->fifosize) + ourport->port.fifosize = ourport->info->fifosize; + ++ /* ++ * DMA transfers must be aligned at least to cache line size, ++ * so find minimal transfer size suitable for DMA mode ++ */ ++ ourport->min_dma_size = max_t(int, ourport->port.fifosize, ++ dma_get_cache_alignment()); ++ + probe_index++; + + dbg("%s: initialising port %p...\n", __func__, ourport); +diff --git a/drivers/tty/serial/samsung.h b/drivers/tty/serial/samsung.h +index d275032aa68d..fc5deaa4f382 100644 +--- a/drivers/tty/serial/samsung.h ++++ b/drivers/tty/serial/samsung.h +@@ -82,6 +82,7 @@ struct s3c24xx_uart_port { + unsigned char tx_claimed; + unsigned int pm_level; + unsigned long baudclk_rate; ++ unsigned int min_dma_size; + + unsigned int rx_irq; + unsigned int tx_irq; +diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c +index 69e769c35cf5..06ecd1e6871c 100644 +--- a/drivers/usb/dwc3/ep0.c ++++ b/drivers/usb/dwc3/ep0.c +@@ -820,6 +820,11 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, + unsigned maxp = ep0->endpoint.maxpacket; + + transfer_size += (maxp - (transfer_size % maxp)); ++ ++ /* Maximum of DWC3_EP0_BOUNCE_SIZE can only be received */ ++ if (transfer_size > DWC3_EP0_BOUNCE_SIZE) ++ transfer_size = DWC3_EP0_BOUNCE_SIZE; ++ + transferred = min_t(u32, ur->length, + transfer_size - length); + memcpy(ur->buf, dwc->ep0_bounce, transferred); +@@ -941,11 +946,14 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, + return; + } + +- WARN_ON(req->request.length > DWC3_EP0_BOUNCE_SIZE); +- + maxpacket = dep->endpoint.maxpacket; + transfer_size = roundup(req->request.length, maxpacket); + ++ if (transfer_size > DWC3_EP0_BOUNCE_SIZE) { ++ dev_WARN(dwc->dev, "bounce buf can't handle req len\n"); ++ transfer_size = DWC3_EP0_BOUNCE_SIZE; ++ } ++ + dwc->ep0_bounced = true; + + /* +diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c +index 531861547253..96d935b00504 100644 +--- a/drivers/usb/gadget/function/f_uac2.c ++++ b/drivers/usb/gadget/function/f_uac2.c +@@ -975,6 +975,29 @@ free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep) + "%s:%d Error!\n", __func__, __LINE__); + } + ++static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, ++ struct usb_endpoint_descriptor *ep_desc, ++ unsigned int factor, bool is_playback) ++{ ++ int chmask, srate, ssize; ++ u16 max_packet_size; ++ ++ if (is_playback) { ++ chmask = uac2_opts->p_chmask; ++ srate = uac2_opts->p_srate; ++ ssize = uac2_opts->p_ssize; ++ } else { ++ chmask = uac2_opts->c_chmask; ++ srate = uac2_opts->c_srate; ++ ssize = uac2_opts->c_ssize; ++ } ++ ++ max_packet_size = num_channels(chmask) * ssize * ++ DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))); ++ ep_desc->wMaxPacketSize = cpu_to_le16(min(max_packet_size, ++ le16_to_cpu(ep_desc->wMaxPacketSize))); ++} ++ + static int + afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) + { +@@ -1070,10 +1093,14 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) + uac2->p_prm.uac2 = uac2; + uac2->c_prm.uac2 = uac2; + ++ /* Calculate wMaxPacketSize according to audio bandwidth */ ++ set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true); ++ set_ep_max_packet_size(uac2_opts, &fs_epout_desc, 1000, false); ++ set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true); ++ set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false); ++ + hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; +- hs_epout_desc.wMaxPacketSize = fs_epout_desc.wMaxPacketSize; + hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; +- hs_epin_desc.wMaxPacketSize = fs_epin_desc.wMaxPacketSize; + + ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL); + if (ret) +diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c +index 309706fe4bf0..9704053dfe05 100644 +--- a/drivers/usb/gadget/udc/m66592-udc.c ++++ b/drivers/usb/gadget/udc/m66592-udc.c +@@ -1052,7 +1052,7 @@ static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl) + tmp = m66592_read(m66592, M66592_INTSTS0) & + M66592_CTSQ; + udelay(1); +- } while (tmp != M66592_CS_IDST || timeout-- > 0); ++ } while (tmp != M66592_CS_IDST && timeout-- > 0); + + if (tmp == M66592_CS_IDST) + m66592_bset(m66592, +diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c +index 5e44407aa099..5216f2b09d63 100644 +--- a/drivers/usb/host/ehci-sysfs.c ++++ b/drivers/usb/host/ehci-sysfs.c +@@ -29,7 +29,7 @@ static ssize_t show_companion(struct device *dev, + int count = PAGE_SIZE; + char *ptr = buf; + +- ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); ++ ehci = hcd_to_ehci(dev_get_drvdata(dev)); + nports = HCS_N_PORTS(ehci->hcs_params); + + for (index = 0; index < nports; ++index) { +@@ -54,7 +54,7 @@ static ssize_t store_companion(struct device *dev, + struct ehci_hcd *ehci; + int portnum, new_owner; + +- ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); ++ ehci = hcd_to_ehci(dev_get_drvdata(dev)); + new_owner = PORT_OWNER; /* Owned by companion */ + if (sscanf(buf, "%d", &portnum) != 1) + return -EINVAL; +@@ -85,7 +85,7 @@ static ssize_t show_uframe_periodic_max(struct device *dev, + struct ehci_hcd *ehci; + int n; + +- ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); ++ ehci = hcd_to_ehci(dev_get_drvdata(dev)); + n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); + return n; + } +@@ -101,7 +101,7 @@ static ssize_t store_uframe_periodic_max(struct device *dev, + unsigned long flags; + ssize_t ret; + +- ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); ++ ehci = hcd_to_ehci(dev_get_drvdata(dev)); + if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) + return -EINVAL; + +diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c +index 4c8b3b82103d..a5a0376bbd48 100644 +--- a/drivers/usb/serial/ftdi_sio.c ++++ b/drivers/usb/serial/ftdi_sio.c +@@ -605,6 +605,10 @@ static const struct usb_device_id id_table_combined[] = { + { USB_DEVICE(FTDI_VID, FTDI_NT_ORIONLXM_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_SYNAPSE_SS200_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX2WI_PID) }, ++ { USB_DEVICE(FTDI_VID, FTDI_CUSTOMWARE_MINIPLEX3_PID) }, + /* + * ELV devices: + */ +diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h +index 792e054126de..2943b97b2a83 100644 +--- a/drivers/usb/serial/ftdi_sio_ids.h ++++ b/drivers/usb/serial/ftdi_sio_ids.h +@@ -568,6 +568,14 @@ + */ + #define FTDI_SYNAPSE_SS200_PID 0x9090 /* SS200 - SNAP Stick 200 */ + ++/* ++ * CustomWare / ShipModul NMEA multiplexers product ids (FTDI_VID) ++ */ ++#define FTDI_CUSTOMWARE_MINIPLEX_PID 0xfd48 /* MiniPlex first generation NMEA Multiplexer */ ++#define FTDI_CUSTOMWARE_MINIPLEX2_PID 0xfd49 /* MiniPlex-USB and MiniPlex-2 series */ ++#define FTDI_CUSTOMWARE_MINIPLEX2WI_PID 0xfd4a /* MiniPlex-2Wi */ ++#define FTDI_CUSTOMWARE_MINIPLEX3_PID 0xfd4b /* MiniPlex-3 series */ ++ + + /********************************/ + /** third-party VID/PID combos **/ +diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c +index f5257af33ecf..ae682e4eeaef 100644 +--- a/drivers/usb/serial/pl2303.c ++++ b/drivers/usb/serial/pl2303.c +@@ -362,21 +362,38 @@ static speed_t pl2303_encode_baud_rate_direct(unsigned char buf[4], + static speed_t pl2303_encode_baud_rate_divisor(unsigned char buf[4], + speed_t baud) + { +- unsigned int tmp; ++ unsigned int baseline, mantissa, exponent; + + /* + * Apparently the formula is: +- * baudrate = 12M * 32 / (2^buf[1]) / buf[0] ++ * baudrate = 12M * 32 / (mantissa * 4^exponent) ++ * where ++ * mantissa = buf[8:0] ++ * exponent = buf[11:9] + */ +- tmp = 12000000 * 32 / baud; ++ baseline = 12000000 * 32; ++ mantissa = baseline / baud; ++ if (mantissa == 0) ++ mantissa = 1; /* Avoid dividing by zero if baud > 32*12M. */ ++ exponent = 0; ++ while (mantissa >= 512) { ++ if (exponent < 7) { ++ mantissa >>= 2; /* divide by 4 */ ++ exponent++; ++ } else { ++ /* Exponent is maxed. Trim mantissa and leave. */ ++ mantissa = 511; ++ break; ++ } ++ } ++ + buf[3] = 0x80; + buf[2] = 0; +- buf[1] = (tmp >= 256); +- while (tmp >= 256) { +- tmp >>= 2; +- buf[1] <<= 1; +- } +- buf[0] = tmp; ++ buf[1] = exponent << 1 | mantissa >> 8; ++ buf[0] = mantissa & 0xff; ++ ++ /* Calculate and return the exact baud rate. */ ++ baud = (baseline / mantissa) >> (exponent << 1); + + return baud; + } +diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c +index d156545728c2..ebcec8cda858 100644 +--- a/drivers/usb/serial/qcserial.c ++++ b/drivers/usb/serial/qcserial.c +@@ -139,6 +139,7 @@ static const struct usb_device_id id_table[] = { + {USB_DEVICE(0x0AF0, 0x8120)}, /* Option GTM681W */ + + /* non-Gobi Sierra Wireless devices */ ++ {DEVICE_SWI(0x03f0, 0x4e1d)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ + {DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */ + {DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */ + {DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */ +diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c +index 8fceec7298e0..6ed804450a5a 100644 +--- a/drivers/usb/serial/symbolserial.c ++++ b/drivers/usb/serial/symbolserial.c +@@ -94,7 +94,7 @@ exit: + + static int symbol_open(struct tty_struct *tty, struct usb_serial_port *port) + { +- struct symbol_private *priv = usb_get_serial_data(port->serial); ++ struct symbol_private *priv = usb_get_serial_port_data(port); + unsigned long flags; + int result = 0; + +@@ -120,7 +120,7 @@ static void symbol_close(struct usb_serial_port *port) + static void symbol_throttle(struct tty_struct *tty) + { + struct usb_serial_port *port = tty->driver_data; +- struct symbol_private *priv = usb_get_serial_data(port->serial); ++ struct symbol_private *priv = usb_get_serial_port_data(port); + + spin_lock_irq(&priv->lock); + priv->throttled = true; +@@ -130,7 +130,7 @@ static void symbol_throttle(struct tty_struct *tty) + static void symbol_unthrottle(struct tty_struct *tty) + { + struct usb_serial_port *port = tty->driver_data; +- struct symbol_private *priv = usb_get_serial_data(port->serial); ++ struct symbol_private *priv = usb_get_serial_port_data(port); + int result; + bool was_throttled; + +diff --git a/fs/ceph/super.c b/fs/ceph/super.c +index d1c833c321b9..7b6bfcbf801c 100644 +--- a/fs/ceph/super.c ++++ b/fs/ceph/super.c +@@ -479,7 +479,7 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) + if (fsopt->max_readdir_bytes != CEPH_MAX_READDIR_BYTES_DEFAULT) + seq_printf(m, ",readdir_max_bytes=%d", fsopt->max_readdir_bytes); + if (strcmp(fsopt->snapdir_name, CEPH_SNAPDIRNAME_DEFAULT)) +- seq_printf(m, ",snapdirname=%s", fsopt->snapdir_name); ++ seq_show_option(m, "snapdirname", fsopt->snapdir_name); + + return 0; + } +diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c +index 0a9fb6b53126..6a1119e87fbb 100644 +--- a/fs/cifs/cifsfs.c ++++ b/fs/cifs/cifsfs.c +@@ -394,17 +394,17 @@ cifs_show_options(struct seq_file *s, struct dentry *root) + struct sockaddr *srcaddr; + srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; + +- seq_printf(s, ",vers=%s", tcon->ses->server->vals->version_string); ++ seq_show_option(s, "vers", tcon->ses->server->vals->version_string); + cifs_show_security(s, tcon->ses); + cifs_show_cache_flavor(s, cifs_sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) + seq_puts(s, ",multiuser"); + else if (tcon->ses->user_name) +- seq_printf(s, ",username=%s", tcon->ses->user_name); ++ seq_show_option(s, "username", tcon->ses->user_name); + + if (tcon->ses->domainName) +- seq_printf(s, ",domain=%s", tcon->ses->domainName); ++ seq_show_option(s, "domain", tcon->ses->domainName); + + if (srcaddr->sa_family != AF_UNSPEC) { + struct sockaddr_in *saddr4; +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index 58987b5c514b..9981064c4a54 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -1763,10 +1763,10 @@ static inline void ext4_show_quota_options(struct seq_file *seq, + } + + if (sbi->s_qf_names[USRQUOTA]) +- seq_printf(seq, ",usrjquota=%s", sbi->s_qf_names[USRQUOTA]); ++ seq_show_option(seq, "usrjquota", sbi->s_qf_names[USRQUOTA]); + + if (sbi->s_qf_names[GRPQUOTA]) +- seq_printf(seq, ",grpjquota=%s", sbi->s_qf_names[GRPQUOTA]); ++ seq_show_option(seq, "grpjquota", sbi->s_qf_names[GRPQUOTA]); + #endif + } + +diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c +index 2982445947e1..894fb01a91da 100644 +--- a/fs/gfs2/super.c ++++ b/fs/gfs2/super.c +@@ -1334,11 +1334,11 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root) + if (is_ancestor(root, sdp->sd_master_dir)) + seq_puts(s, ",meta"); + if (args->ar_lockproto[0]) +- seq_printf(s, ",lockproto=%s", args->ar_lockproto); ++ seq_show_option(s, "lockproto", args->ar_lockproto); + if (args->ar_locktable[0]) +- seq_printf(s, ",locktable=%s", args->ar_locktable); ++ seq_show_option(s, "locktable", args->ar_locktable); + if (args->ar_hostdata[0]) +- seq_printf(s, ",hostdata=%s", args->ar_hostdata); ++ seq_show_option(s, "hostdata", args->ar_hostdata); + if (args->ar_spectator) + seq_puts(s, ",spectator"); + if (args->ar_localflocks) +diff --git a/fs/hfs/super.c b/fs/hfs/super.c +index 55c03b9e9070..4574fdd3d421 100644 +--- a/fs/hfs/super.c ++++ b/fs/hfs/super.c +@@ -136,9 +136,9 @@ static int hfs_show_options(struct seq_file *seq, struct dentry *root) + struct hfs_sb_info *sbi = HFS_SB(root->d_sb); + + if (sbi->s_creator != cpu_to_be32(0x3f3f3f3f)) +- seq_printf(seq, ",creator=%.4s", (char *)&sbi->s_creator); ++ seq_show_option_n(seq, "creator", (char *)&sbi->s_creator, 4); + if (sbi->s_type != cpu_to_be32(0x3f3f3f3f)) +- seq_printf(seq, ",type=%.4s", (char *)&sbi->s_type); ++ seq_show_option_n(seq, "type", (char *)&sbi->s_type, 4); + seq_printf(seq, ",uid=%u,gid=%u", + from_kuid_munged(&init_user_ns, sbi->s_uid), + from_kgid_munged(&init_user_ns, sbi->s_gid)); +diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c +index c90b72ee676d..bb806e58c977 100644 +--- a/fs/hfsplus/options.c ++++ b/fs/hfsplus/options.c +@@ -218,9 +218,9 @@ int hfsplus_show_options(struct seq_file *seq, struct dentry *root) + struct hfsplus_sb_info *sbi = HFSPLUS_SB(root->d_sb); + + if (sbi->creator != HFSPLUS_DEF_CR_TYPE) +- seq_printf(seq, ",creator=%.4s", (char *)&sbi->creator); ++ seq_show_option_n(seq, "creator", (char *)&sbi->creator, 4); + if (sbi->type != HFSPLUS_DEF_CR_TYPE) +- seq_printf(seq, ",type=%.4s", (char *)&sbi->type); ++ seq_show_option_n(seq, "type", (char *)&sbi->type, 4); + seq_printf(seq, ",umask=%o,uid=%u,gid=%u", sbi->umask, + from_kuid_munged(&init_user_ns, sbi->uid), + from_kgid_munged(&init_user_ns, sbi->gid)); +diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c +index 059597b23f67..2ac99db3750e 100644 +--- a/fs/hostfs/hostfs_kern.c ++++ b/fs/hostfs/hostfs_kern.c +@@ -260,7 +260,7 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root) + size_t offset = strlen(root_ino) + 1; + + if (strlen(root_path) > offset) +- seq_printf(seq, ",%s", root_path + offset); ++ seq_show_option(seq, root_path + offset, NULL); + + if (append) + seq_puts(seq, ",append"); +diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c +index a0872f239f04..9e92c9c2d319 100644 +--- a/fs/hpfs/namei.c ++++ b/fs/hpfs/namei.c +@@ -8,6 +8,17 @@ + #include + #include "hpfs_fn.h" + ++static void hpfs_update_directory_times(struct inode *dir) ++{ ++ time_t t = get_seconds(); ++ if (t == dir->i_mtime.tv_sec && ++ t == dir->i_ctime.tv_sec) ++ return; ++ dir->i_mtime.tv_sec = dir->i_ctime.tv_sec = t; ++ dir->i_mtime.tv_nsec = dir->i_ctime.tv_nsec = 0; ++ hpfs_write_inode_nolock(dir); ++} ++ + static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) + { + const unsigned char *name = dentry->d_name.name; +@@ -99,6 +110,7 @@ static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) + result->i_mode = mode | S_IFDIR; + hpfs_write_inode_nolock(result); + } ++ hpfs_update_directory_times(dir); + d_instantiate(dentry, result); + hpfs_unlock(dir->i_sb); + return 0; +@@ -187,6 +199,7 @@ static int hpfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, b + result->i_mode = mode | S_IFREG; + hpfs_write_inode_nolock(result); + } ++ hpfs_update_directory_times(dir); + d_instantiate(dentry, result); + hpfs_unlock(dir->i_sb); + return 0; +@@ -262,6 +275,7 @@ static int hpfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, de + insert_inode_hash(result); + + hpfs_write_inode_nolock(result); ++ hpfs_update_directory_times(dir); + d_instantiate(dentry, result); + brelse(bh); + hpfs_unlock(dir->i_sb); +@@ -340,6 +354,7 @@ static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *sy + insert_inode_hash(result); + + hpfs_write_inode_nolock(result); ++ hpfs_update_directory_times(dir); + d_instantiate(dentry, result); + hpfs_unlock(dir->i_sb); + return 0; +@@ -423,6 +438,8 @@ again: + out1: + hpfs_brelse4(&qbh); + out: ++ if (!err) ++ hpfs_update_directory_times(dir); + hpfs_unlock(dir->i_sb); + return err; + } +@@ -477,6 +494,8 @@ static int hpfs_rmdir(struct inode *dir, struct dentry *dentry) + out1: + hpfs_brelse4(&qbh); + out: ++ if (!err) ++ hpfs_update_directory_times(dir); + hpfs_unlock(dir->i_sb); + return err; + } +@@ -595,7 +614,7 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry, + goto end1; + } + +- end: ++end: + hpfs_i(i)->i_parent_dir = new_dir->i_ino; + if (S_ISDIR(i->i_mode)) { + inc_nlink(new_dir); +@@ -610,6 +629,10 @@ static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry, + brelse(bh); + } + end1: ++ if (!err) { ++ hpfs_update_directory_times(old_dir); ++ hpfs_update_directory_times(new_dir); ++ } + hpfs_unlock(i->i_sb); + return err; + } +diff --git a/fs/libfs.c b/fs/libfs.c +index 102edfd39000..c7cbfb092e94 100644 +--- a/fs/libfs.c ++++ b/fs/libfs.c +@@ -1185,7 +1185,7 @@ void make_empty_dir_inode(struct inode *inode) + inode->i_uid = GLOBAL_ROOT_UID; + inode->i_gid = GLOBAL_ROOT_GID; + inode->i_rdev = 0; +- inode->i_size = 2; ++ inode->i_size = 0; + inode->i_blkbits = PAGE_SHIFT; + inode->i_blocks = 0; + +diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c +index 719f7f4c7a37..33efa334ec76 100644 +--- a/fs/ocfs2/file.c ++++ b/fs/ocfs2/file.c +@@ -2372,6 +2372,20 @@ relock: + /* buffered aio wouldn't have proper lock coverage today */ + BUG_ON(written == -EIOCBQUEUED && !(iocb->ki_flags & IOCB_DIRECT)); + ++ /* ++ * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io ++ * function pointer which is called when o_direct io completes so that ++ * it can unlock our rw lock. ++ * Unfortunately there are error cases which call end_io and others ++ * that don't. so we don't have to unlock the rw_lock if either an ++ * async dio is going to do it in the future or an end_io after an ++ * error has already done it. ++ */ ++ if ((written == -EIOCBQUEUED) || (!ocfs2_iocb_is_rw_locked(iocb))) { ++ rw_level = -1; ++ unaligned_dio = 0; ++ } ++ + if (unlikely(written <= 0)) + goto no_sync; + +@@ -2396,20 +2410,6 @@ relock: + } + + no_sync: +- /* +- * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io +- * function pointer which is called when o_direct io completes so that +- * it can unlock our rw lock. +- * Unfortunately there are error cases which call end_io and others +- * that don't. so we don't have to unlock the rw_lock if either an +- * async dio is going to do it in the future or an end_io after an +- * error has already done it. +- */ +- if ((ret == -EIOCBQUEUED) || (!ocfs2_iocb_is_rw_locked(iocb))) { +- rw_level = -1; +- unaligned_dio = 0; +- } +- + if (unaligned_dio) { + ocfs2_iocb_clear_unaligned_aio(iocb); + mutex_unlock(&OCFS2_I(inode)->ip_unaligned_aio); +diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c +index 403c5660b306..a482e312c7b2 100644 +--- a/fs/ocfs2/super.c ++++ b/fs/ocfs2/super.c +@@ -1550,8 +1550,8 @@ static int ocfs2_show_options(struct seq_file *s, struct dentry *root) + seq_printf(s, ",localflocks,"); + + if (osb->osb_cluster_stack[0]) +- seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, +- osb->osb_cluster_stack); ++ seq_show_option_n(s, "cluster_stack", osb->osb_cluster_stack, ++ OCFS2_STACK_LABEL_LEN); + if (opts & OCFS2_MOUNT_USRQUOTA) + seq_printf(s, ",usrquota"); + if (opts & OCFS2_MOUNT_GRPQUOTA) +diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c +index 7466ff339c66..79073d68b475 100644 +--- a/fs/overlayfs/super.c ++++ b/fs/overlayfs/super.c +@@ -588,10 +588,10 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) + struct super_block *sb = dentry->d_sb; + struct ovl_fs *ufs = sb->s_fs_info; + +- seq_printf(m, ",lowerdir=%s", ufs->config.lowerdir); ++ seq_show_option(m, "lowerdir", ufs->config.lowerdir); + if (ufs->config.upperdir) { +- seq_printf(m, ",upperdir=%s", ufs->config.upperdir); +- seq_printf(m, ",workdir=%s", ufs->config.workdir); ++ seq_show_option(m, "upperdir", ufs->config.upperdir); ++ seq_show_option(m, "workdir", ufs->config.workdir); + } + return 0; + } +diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c +index 0e4cf728126f..4a62fe8cc3bf 100644 +--- a/fs/reiserfs/super.c ++++ b/fs/reiserfs/super.c +@@ -714,18 +714,20 @@ static int reiserfs_show_options(struct seq_file *seq, struct dentry *root) + seq_puts(seq, ",acl"); + + if (REISERFS_SB(s)->s_jdev) +- seq_printf(seq, ",jdev=%s", REISERFS_SB(s)->s_jdev); ++ seq_show_option(seq, "jdev", REISERFS_SB(s)->s_jdev); + + if (journal->j_max_commit_age != journal->j_default_max_commit_age) + seq_printf(seq, ",commit=%d", journal->j_max_commit_age); + + #ifdef CONFIG_QUOTA + if (REISERFS_SB(s)->s_qf_names[USRQUOTA]) +- seq_printf(seq, ",usrjquota=%s", REISERFS_SB(s)->s_qf_names[USRQUOTA]); ++ seq_show_option(seq, "usrjquota", ++ REISERFS_SB(s)->s_qf_names[USRQUOTA]); + else if (opts & (1 << REISERFS_USRQUOTA)) + seq_puts(seq, ",usrquota"); + if (REISERFS_SB(s)->s_qf_names[GRPQUOTA]) +- seq_printf(seq, ",grpjquota=%s", REISERFS_SB(s)->s_qf_names[GRPQUOTA]); ++ seq_show_option(seq, "grpjquota", ++ REISERFS_SB(s)->s_qf_names[GRPQUOTA]); + else if (opts & (1 << REISERFS_GRPQUOTA)) + seq_puts(seq, ",grpquota"); + if (REISERFS_SB(s)->s_jquota_fmt) { +diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h +index 74bcbabfa523..b14bbd6bb05f 100644 +--- a/fs/xfs/libxfs/xfs_da_format.h ++++ b/fs/xfs/libxfs/xfs_da_format.h +@@ -680,8 +680,15 @@ typedef struct xfs_attr_leaf_name_remote { + typedef struct xfs_attr_leafblock { + xfs_attr_leaf_hdr_t hdr; /* constant-structure header block */ + xfs_attr_leaf_entry_t entries[1]; /* sorted on key, not name */ +- xfs_attr_leaf_name_local_t namelist; /* grows from bottom of buf */ +- xfs_attr_leaf_name_remote_t valuelist; /* grows from bottom of buf */ ++ /* ++ * The rest of the block contains the following structures after the ++ * leaf entries, growing from the bottom up. The variables are never ++ * referenced and definining them can actually make gcc optimize away ++ * accesses to the 'entries' array above index 0 so don't do that. ++ * ++ * xfs_attr_leaf_name_local_t namelist; ++ * xfs_attr_leaf_name_remote_t valuelist; ++ */ + } xfs_attr_leafblock_t; + + /* +diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c +index de1ea16f5748..534bbf283d6b 100644 +--- a/fs/xfs/libxfs/xfs_dir2_data.c ++++ b/fs/xfs/libxfs/xfs_dir2_data.c +@@ -252,7 +252,8 @@ xfs_dir3_data_reada_verify( + return; + case cpu_to_be32(XFS_DIR2_DATA_MAGIC): + case cpu_to_be32(XFS_DIR3_DATA_MAGIC): +- xfs_dir3_data_verify(bp); ++ bp->b_ops = &xfs_dir3_data_buf_ops; ++ bp->b_ops->verify_read(bp); + return; + default: + xfs_buf_ioerror(bp, -EFSCORRUPTED); +diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c +index 41b80d3d3877..06bb4218b362 100644 +--- a/fs/xfs/libxfs/xfs_dir2_node.c ++++ b/fs/xfs/libxfs/xfs_dir2_node.c +@@ -2132,6 +2132,7 @@ xfs_dir2_node_replace( + int error; /* error return value */ + int i; /* btree level */ + xfs_ino_t inum; /* new inode number */ ++ int ftype; /* new file type */ + xfs_dir2_leaf_t *leaf; /* leaf structure */ + xfs_dir2_leaf_entry_t *lep; /* leaf entry being changed */ + int rval; /* internal return value */ +@@ -2145,7 +2146,14 @@ xfs_dir2_node_replace( + state = xfs_da_state_alloc(); + state->args = args; + state->mp = args->dp->i_mount; ++ ++ /* ++ * We have to save new inode number and ftype since ++ * xfs_da3_node_lookup_int() is going to overwrite them ++ */ + inum = args->inumber; ++ ftype = args->filetype; ++ + /* + * Lookup the entry to change in the btree. + */ +@@ -2183,7 +2191,7 @@ xfs_dir2_node_replace( + * Fill in the new inode number and log the entry. + */ + dep->inumber = cpu_to_be64(inum); +- args->dp->d_ops->data_put_ftype(dep, args->filetype); ++ args->dp->d_ops->data_put_ftype(dep, ftype); + xfs_dir2_data_log_entry(args, state->extrablk.bp, dep); + rval = 0; + } +diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c +index 3859f5e27a4d..458fced2c0f9 100644 +--- a/fs/xfs/xfs_aops.c ++++ b/fs/xfs/xfs_aops.c +@@ -356,7 +356,8 @@ xfs_end_bio( + { + xfs_ioend_t *ioend = bio->bi_private; + +- ioend->io_error = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : error; ++ if (!ioend->io_error && !test_bit(BIO_UPTODATE, &bio->bi_flags)) ++ ioend->io_error = error; + + /* Toss bio and pass work off to an xfsdatad thread */ + bio->bi_private = NULL; +diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c +index 1fb16562c159..bbd9b1f10ffb 100644 +--- a/fs/xfs/xfs_super.c ++++ b/fs/xfs/xfs_super.c +@@ -511,9 +511,9 @@ xfs_showargs( + seq_printf(m, "," MNTOPT_LOGBSIZE "=%dk", mp->m_logbsize >> 10); + + if (mp->m_logname) +- seq_printf(m, "," MNTOPT_LOGDEV "=%s", mp->m_logname); ++ seq_show_option(m, MNTOPT_LOGDEV, mp->m_logname); + if (mp->m_rtname) +- seq_printf(m, "," MNTOPT_RTDEV "=%s", mp->m_rtname); ++ seq_show_option(m, MNTOPT_RTDEV, mp->m_rtname); + + if (mp->m_dalign > 0) + seq_printf(m, "," MNTOPT_SUNIT "=%d", +diff --git a/include/linux/acpi.h b/include/linux/acpi.h +index d2445fa9999f..0b2394f61af4 100644 +--- a/include/linux/acpi.h ++++ b/include/linux/acpi.h +@@ -221,7 +221,7 @@ struct pci_dev; + + int acpi_pci_irq_enable (struct pci_dev *dev); + void acpi_penalize_isa_irq(int irq, int active); +- ++void acpi_penalize_sci_irq(int irq, int trigger, int polarity); + void acpi_pci_irq_disable (struct pci_dev *dev); + + extern int ec_read(u8 addr, u8 *val); +diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h +index f79148261d16..7bb7f673cb3f 100644 +--- a/include/linux/iio/iio.h ++++ b/include/linux/iio/iio.h +@@ -645,6 +645,15 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, + #define IIO_DEGREE_TO_RAD(deg) (((deg) * 314159ULL + 9000000ULL) / 18000000ULL) + + /** ++ * IIO_RAD_TO_DEGREE() - Convert rad to degree ++ * @rad: A value in rad ++ * ++ * Returns the given value converted from rad to degree ++ */ ++#define IIO_RAD_TO_DEGREE(rad) \ ++ (((rad) * 18000000ULL + 314159ULL / 2) / 314159ULL) ++ ++/** + * IIO_G_TO_M_S_2() - Convert g to meter / second**2 + * @g: A value in g + * +@@ -652,4 +661,12 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, + */ + #define IIO_G_TO_M_S_2(g) ((g) * 980665ULL / 100000ULL) + ++/** ++ * IIO_M_S_2_TO_G() - Convert meter / second**2 to g ++ * @ms2: A value in meter / second**2 ++ * ++ * Returns the given value converted from meter / second**2 to g ++ */ ++#define IIO_M_S_2_TO_G(ms2) (((ms2) * 100000ULL + 980665ULL / 2) / 980665ULL) ++ + #endif /* _INDUSTRIAL_IO_H_ */ +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 860c751810fc..1d4eb6057f72 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -180,6 +180,8 @@ enum pci_dev_flags { + PCI_DEV_FLAGS_NO_BUS_RESET = (__force pci_dev_flags_t) (1 << 6), + /* Do not use PM reset even if device advertises NoSoftRst- */ + PCI_DEV_FLAGS_NO_PM_RESET = (__force pci_dev_flags_t) (1 << 7), ++ /* Get VPD from function 0 VPD */ ++ PCI_DEV_FLAGS_VPD_REF_F0 = (__force pci_dev_flags_t) (1 << 8), + }; + + enum pci_irq_reroute_variant { +diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h +index 912a7c482649..d4c7271382cb 100644 +--- a/include/linux/seq_file.h ++++ b/include/linux/seq_file.h +@@ -149,6 +149,41 @@ static inline struct user_namespace *seq_user_ns(struct seq_file *seq) + #endif + } + ++/** ++ * seq_show_options - display mount options with appropriate escapes. ++ * @m: the seq_file handle ++ * @name: the mount option name ++ * @value: the mount option name's value, can be NULL ++ */ ++static inline void seq_show_option(struct seq_file *m, const char *name, ++ const char *value) ++{ ++ seq_putc(m, ','); ++ seq_escape(m, name, ",= \t\n\\"); ++ if (value) { ++ seq_putc(m, '='); ++ seq_escape(m, value, ", \t\n\\"); ++ } ++} ++ ++/** ++ * seq_show_option_n - display mount options with appropriate escapes ++ * where @value must be a specific length. ++ * @m: the seq_file handle ++ * @name: the mount option name ++ * @value: the mount option name's value, cannot be NULL ++ * @length: the length of @value to display ++ * ++ * This is a macro since this uses "length" to define the size of the ++ * stack buffer. ++ */ ++#define seq_show_option_n(m, name, value, length) { \ ++ char val_buf[length + 1]; \ ++ strncpy(val_buf, value, length); \ ++ val_buf[length] = '\0'; \ ++ seq_show_option(m, name, val_buf); \ ++} ++ + #define SEQ_START_TOKEN ((void *)1) + /* + * Helpers for iteration over list_head-s in seq_files +diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h +index 061aca3a962d..d34611e35a30 100644 +--- a/include/uapi/linux/dm-ioctl.h ++++ b/include/uapi/linux/dm-ioctl.h +@@ -267,9 +267,9 @@ enum { + #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) + + #define DM_VERSION_MAJOR 4 +-#define DM_VERSION_MINOR 32 ++#define DM_VERSION_MINOR 33 + #define DM_VERSION_PATCHLEVEL 0 +-#define DM_VERSION_EXTRA "-ioctl (2015-6-26)" ++#define DM_VERSION_EXTRA "-ioctl (2015-8-18)" + + /* Status bits */ + #define DM_READONLY_FLAG (1 << 0) /* In/Out */ +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index f89d9292eee6..c6c4240e7d28 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -1334,7 +1334,7 @@ static int cgroup_show_options(struct seq_file *seq, + + for_each_subsys(ss, ssid) + if (root->subsys_mask & (1 << ssid)) +- seq_printf(seq, ",%s", ss->name); ++ seq_show_option(seq, ss->name, NULL); + if (root->flags & CGRP_ROOT_NOPREFIX) + seq_puts(seq, ",noprefix"); + if (root->flags & CGRP_ROOT_XATTR) +@@ -1342,13 +1342,14 @@ static int cgroup_show_options(struct seq_file *seq, + + spin_lock(&release_agent_path_lock); + if (strlen(root->release_agent_path)) +- seq_printf(seq, ",release_agent=%s", root->release_agent_path); ++ seq_show_option(seq, "release_agent", ++ root->release_agent_path); + spin_unlock(&release_agent_path_lock); + + if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags)) + seq_puts(seq, ",clone_children"); + if (strlen(root->name)) +- seq_printf(seq, ",name=%s", root->name); ++ seq_show_option(seq, "name", root->name); + return 0; + } + +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index 78b4bad10081..e9673433cc01 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -5433,6 +5433,14 @@ static int sched_cpu_active(struct notifier_block *nfb, + case CPU_STARTING: + set_cpu_rq_start_time(); + return NOTIFY_OK; ++ case CPU_ONLINE: ++ /* ++ * At this point a starting CPU has marked itself as online via ++ * set_cpu_online(). But it might not yet have marked itself ++ * as active, which is essential from here on. ++ * ++ * Thus, fall-through and help the starting CPU along. ++ */ + case CPU_DOWN_FAILED: + set_cpu_active((long)hcpu, true); + return NOTIFY_OK; +diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c +index 6da82bcb0a8b..8fd97dac538a 100644 +--- a/mm/memory_hotplug.c ++++ b/mm/memory_hotplug.c +@@ -1248,6 +1248,14 @@ int __ref add_memory(int nid, u64 start, u64 size) + + mem_hotplug_begin(); + ++ /* ++ * Add new range to memblock so that when hotadd_new_pgdat() is called ++ * to allocate new pgdat, get_pfn_range_for_nid() will be able to find ++ * this new range and calculate total pages correctly. The range will ++ * be removed at hot-remove time. ++ */ ++ memblock_add_node(start, size, nid); ++ + new_node = !node_online(nid); + if (new_node) { + pgdat = hotadd_new_pgdat(nid, start); +@@ -1277,7 +1285,6 @@ int __ref add_memory(int nid, u64 start, u64 size) + + /* create new memmap entry */ + firmware_map_add_hotplug(start, start + size, "System RAM"); +- memblock_add_node(start, size, nid); + + goto out; + +@@ -1286,6 +1293,7 @@ error: + if (new_pgdat) + rollback_node_hotadd(nid, pgdat); + release_memory_resource(res); ++ memblock_remove(start, size); + + out: + mem_hotplug_done(); +diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c +index f30329f72641..69a4d30a9ccf 100644 +--- a/net/ceph/ceph_common.c ++++ b/net/ceph/ceph_common.c +@@ -517,8 +517,11 @@ int ceph_print_client_options(struct seq_file *m, struct ceph_client *client) + struct ceph_options *opt = client->options; + size_t pos = m->count; + +- if (opt->name) +- seq_printf(m, "name=%s,", opt->name); ++ if (opt->name) { ++ seq_puts(m, "name="); ++ seq_escape(m, opt->name, ", \t\n\\"); ++ seq_putc(m, ','); ++ } + if (opt->key) + seq_puts(m, "secret=,"); + +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index 564079c5c49d..cdf4c589a391 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -1100,7 +1100,7 @@ static void selinux_write_opts(struct seq_file *m, + seq_puts(m, prefix); + if (has_comma) + seq_putc(m, '\"'); +- seq_puts(m, opts->mnt_opts[i]); ++ seq_escape(m, opts->mnt_opts[i], "\"\n\\"); + if (has_comma) + seq_putc(m, '\"'); + } +diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c +index 36d842570745..69c63b92e078 100644 +--- a/sound/soc/codecs/adav80x.c ++++ b/sound/soc/codecs/adav80x.c +@@ -865,7 +865,6 @@ const struct regmap_config adav80x_regmap_config = { + .val_bits = 8, + .pad_bits = 1, + .reg_bits = 7, +- .read_flag_mask = 0x01, + + .max_register = ADAV80X_PLL_OUTE, + +diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c +index 802e05eae3e9..4180827a8480 100644 +--- a/sound/soc/codecs/arizona.c ++++ b/sound/soc/codecs/arizona.c +@@ -1756,17 +1756,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) + } + EXPORT_SYMBOL_GPL(arizona_init_dai); + +-static irqreturn_t arizona_fll_clock_ok(int irq, void *data) +-{ +- struct arizona_fll *fll = data; +- +- arizona_fll_dbg(fll, "clock OK\n"); +- +- complete(&fll->ok); +- +- return IRQ_HANDLED; +-} +- + static struct { + unsigned int min; + unsigned int max; +@@ -2048,17 +2037,18 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll) + static int arizona_enable_fll(struct arizona_fll *fll) + { + struct arizona *arizona = fll->arizona; +- unsigned long time_left; + bool use_sync = false; + int already_enabled = arizona_is_enabled_fll(fll); + struct arizona_fll_cfg cfg; ++ int i; ++ unsigned int val; + + if (already_enabled < 0) + return already_enabled; + + if (already_enabled) { + /* Facilitate smooth refclk across the transition */ +- regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x7, ++ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9, + ARIZONA_FLL1_GAIN_MASK, 0); + regmap_update_bits_async(fll->arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, +@@ -2110,9 +2100,6 @@ static int arizona_enable_fll(struct arizona_fll *fll) + if (!already_enabled) + pm_runtime_get(arizona->dev); + +- /* Clear any pending completions */ +- try_wait_for_completion(&fll->ok); +- + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA); + if (use_sync) +@@ -2124,10 +2111,24 @@ static int arizona_enable_fll(struct arizona_fll *fll) + regmap_update_bits_async(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + +- time_left = wait_for_completion_timeout(&fll->ok, +- msecs_to_jiffies(250)); +- if (time_left == 0) ++ arizona_fll_dbg(fll, "Waiting for FLL lock...\n"); ++ val = 0; ++ for (i = 0; i < 15; i++) { ++ if (i < 5) ++ usleep_range(200, 400); ++ else ++ msleep(20); ++ ++ regmap_read(arizona->regmap, ++ ARIZONA_INTERRUPT_RAW_STATUS_5, ++ &val); ++ if (val & (ARIZONA_FLL1_CLOCK_OK_STS << (fll->id - 1))) ++ break; ++ } ++ if (i == 15) + arizona_fll_warn(fll, "Timed out waiting for lock\n"); ++ else ++ arizona_fll_dbg(fll, "FLL locked (%d polls)\n", i); + + return 0; + } +@@ -2212,11 +2213,8 @@ EXPORT_SYMBOL_GPL(arizona_set_fll); + int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, + int ok_irq, struct arizona_fll *fll) + { +- int ret; + unsigned int val; + +- init_completion(&fll->ok); +- + fll->id = id; + fll->base = base; + fll->arizona = arizona; +@@ -2238,13 +2236,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, + snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), + "FLL%d clock OK", id); + +- ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, +- arizona_fll_clock_ok, fll); +- if (ret != 0) { +- dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n", +- id, ret); +- } +- + regmap_update_bits(arizona->regmap, fll->base + 1, + ARIZONA_FLL1_FREERUN, 0); + +diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h +index 43deb0462309..36867d05e0bb 100644 +--- a/sound/soc/codecs/arizona.h ++++ b/sound/soc/codecs/arizona.h +@@ -242,7 +242,6 @@ struct arizona_fll { + int id; + unsigned int base; + unsigned int vco_mult; +- struct completion ok; + + unsigned int fout; + int sync_src; +diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c +index 9bc78e57513d..ff72cd8c236e 100644 +--- a/sound/soc/codecs/rt5640.c ++++ b/sound/soc/codecs/rt5640.c +@@ -984,6 +984,35 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w, + return 0; + } + ++static int rt5640_lout_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); ++ ++ switch (event) { ++ case SND_SOC_DAPM_POST_PMU: ++ hp_amp_power_on(codec); ++ snd_soc_update_bits(codec, RT5640_PWR_ANLG1, ++ RT5640_PWR_LM, RT5640_PWR_LM); ++ snd_soc_update_bits(codec, RT5640_OUTPUT, ++ RT5640_L_MUTE | RT5640_R_MUTE, 0); ++ break; ++ ++ case SND_SOC_DAPM_PRE_PMD: ++ snd_soc_update_bits(codec, RT5640_OUTPUT, ++ RT5640_L_MUTE | RT5640_R_MUTE, ++ RT5640_L_MUTE | RT5640_R_MUTE); ++ snd_soc_update_bits(codec, RT5640_PWR_ANLG1, ++ RT5640_PWR_LM, 0); ++ break; ++ ++ default: ++ return 0; ++ } ++ ++ return 0; ++} ++ + static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) + { +@@ -1179,13 +1208,16 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { + 0, rt5640_spo_l_mix, ARRAY_SIZE(rt5640_spo_l_mix)), + SND_SOC_DAPM_MIXER("SPOR MIX", SND_SOC_NOPM, 0, + 0, rt5640_spo_r_mix, ARRAY_SIZE(rt5640_spo_r_mix)), +- SND_SOC_DAPM_MIXER("LOUT MIX", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0, ++ SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0, + rt5640_lout_mix, ARRAY_SIZE(rt5640_lout_mix)), + SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM, + 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0, + rt5640_hp_event, + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), ++ SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0, ++ rt5640_lout_event, ++ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1, + RT5640_PWR_HP_L_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1, +@@ -1500,8 +1532,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = { + {"HP R Playback", "Switch", "HP Amp"}, + {"HPOL", NULL, "HP L Playback"}, + {"HPOR", NULL, "HP R Playback"}, +- {"LOUTL", NULL, "LOUT MIX"}, +- {"LOUTR", NULL, "LOUT MIX"}, ++ ++ {"LOUT amp", NULL, "LOUT MIX"}, ++ {"LOUTL", NULL, "LOUT amp"}, ++ {"LOUTR", NULL, "LOUT amp"}, + }; + + static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = { +diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c +index 961bd7e5877e..58713733d314 100644 +--- a/sound/soc/codecs/rt5645.c ++++ b/sound/soc/codecs/rt5645.c +@@ -3232,6 +3232,13 @@ static struct dmi_system_id dmi_platform_intel_braswell[] = { + DMI_MATCH(DMI_PRODUCT_NAME, "Strago"), + }, + }, ++ { ++ .ident = "Google Celes", ++ .callback = strago_quirk_cb, ++ .matches = { ++ DMI_MATCH(DMI_PRODUCT_NAME, "Celes"), ++ }, ++ }, + { } + }; + +diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c +index 8bf2e2c4bafb..9e371eb3e4fa 100644 +--- a/sound/soc/samsung/arndale_rt5631.c ++++ b/sound/soc/samsung/arndale_rt5631.c +@@ -116,15 +116,6 @@ static int arndale_audio_probe(struct platform_device *pdev) + return ret; + } + +-static int arndale_audio_remove(struct platform_device *pdev) +-{ +- struct snd_soc_card *card = platform_get_drvdata(pdev); +- +- snd_soc_unregister_card(card); +- +- return 0; +-} +- + static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = { + { .compatible = "samsung,arndale-rt5631", }, + { .compatible = "samsung,arndale-alc5631", }, +@@ -139,7 +130,6 @@ static struct platform_driver arndale_audio_driver = { + .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match), + }, + .probe = arndale_audio_probe, +- .remove = arndale_audio_remove, + }; + + module_platform_driver(arndale_audio_driver); diff --git a/patch/kernel/odroid-next/patch-4.2.1-2.patch b/patch/kernel/odroid-next/patch-4.2.1-2.patch new file mode 100644 index 000000000..38d74b591 --- /dev/null +++ b/patch/kernel/odroid-next/patch-4.2.1-2.patch @@ -0,0 +1,4802 @@ +diff --git a/Makefile b/Makefile +index a03efc18aa48..3578b4426ecf 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 2 +-SUBLEVEL = 1 ++SUBLEVEL = 2 + EXTRAVERSION = + NAME = Hurr durr I'ma sheep + +diff --git a/arch/arm/boot/compressed/decompress.c b/arch/arm/boot/compressed/decompress.c +index bd245d34952d..a0765e7ed6c7 100644 +--- a/arch/arm/boot/compressed/decompress.c ++++ b/arch/arm/boot/compressed/decompress.c +@@ -57,5 +57,5 @@ extern char * strstr(const char * s1, const char *s2); + + int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x)) + { +- return decompress(input, len, NULL, NULL, output, NULL, error); ++ return __decompress(input, len, NULL, NULL, output, 0, NULL, error); + } +diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c +index bc738d2b8392..f9c341c5ae78 100644 +--- a/arch/arm/kvm/arm.c ++++ b/arch/arm/kvm/arm.c +@@ -449,7 +449,7 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu) + * Map the VGIC hardware resources before running a vcpu the first + * time on this VM. + */ +- if (unlikely(!vgic_ready(kvm))) { ++ if (unlikely(irqchip_in_kernel(kvm) && !vgic_ready(kvm))) { + ret = kvm_vgic_map_resources(kvm); + if (ret) + return ret; +diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig +index 318175f62c24..735456feb08e 100644 +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -104,6 +104,10 @@ config NO_IOPORT_MAP + config STACKTRACE_SUPPORT + def_bool y + ++config ILLEGAL_POINTER_VALUE ++ hex ++ default 0xdead000000000000 ++ + config LOCKDEP_SUPPORT + def_bool y + +@@ -417,6 +421,22 @@ config ARM64_ERRATUM_845719 + + If unsure, say Y. + ++config ARM64_ERRATUM_843419 ++ bool "Cortex-A53: 843419: A load or store might access an incorrect address" ++ depends on MODULES ++ default y ++ help ++ This option builds kernel modules using the large memory model in ++ order to avoid the use of the ADRP instruction, which can cause ++ a subsequent memory access to use an incorrect address on Cortex-A53 ++ parts up to r0p4. ++ ++ Note that the kernel itself must be linked with a version of ld ++ which fixes potentially affected ADRP instructions through the ++ use of veneers. ++ ++ If unsure, say Y. ++ + endmenu + + +diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile +index 4d2a925998f9..81151663ef38 100644 +--- a/arch/arm64/Makefile ++++ b/arch/arm64/Makefile +@@ -30,6 +30,10 @@ endif + + CHECKFLAGS += -D__aarch64__ + ++ifeq ($(CONFIG_ARM64_ERRATUM_843419), y) ++CFLAGS_MODULE += -mcmodel=large ++endif ++ + # Default value + head-y := arch/arm64/kernel/head.o + +diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h +index f800d45ea226..44a59c20e773 100644 +--- a/arch/arm64/include/asm/memory.h ++++ b/arch/arm64/include/asm/memory.h +@@ -114,6 +114,14 @@ extern phys_addr_t memstart_addr; + #define PHYS_OFFSET ({ memstart_addr; }) + + /* ++ * The maximum physical address that the linear direct mapping ++ * of system RAM can cover. (PAGE_OFFSET can be interpreted as ++ * a 2's complement signed quantity and negated to derive the ++ * maximum size of the linear mapping.) ++ */ ++#define MAX_MEMBLOCK_ADDR ({ memstart_addr - PAGE_OFFSET - 1; }) ++ ++/* + * PFNs are used to describe any physical page; this means + * PFN 0 == physical address 0. + * +diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S +index e16351819fed..8213ca15abd2 100644 +--- a/arch/arm64/kernel/entry.S ++++ b/arch/arm64/kernel/entry.S +@@ -116,7 +116,7 @@ + */ + .endm + +- .macro kernel_exit, el, ret = 0 ++ .macro kernel_exit, el + ldp x21, x22, [sp, #S_PC] // load ELR, SPSR + .if \el == 0 + ct_user_enter +@@ -146,11 +146,7 @@ + .endif + msr elr_el1, x21 // set up the return data + msr spsr_el1, x22 +- .if \ret +- ldr x1, [sp, #S_X1] // preserve x0 (syscall return) +- .else + ldp x0, x1, [sp, #16 * 0] +- .endif + ldp x2, x3, [sp, #16 * 1] + ldp x4, x5, [sp, #16 * 2] + ldp x6, x7, [sp, #16 * 3] +@@ -613,22 +609,21 @@ ENDPROC(cpu_switch_to) + */ + ret_fast_syscall: + disable_irq // disable interrupts ++ str x0, [sp, #S_X0] // returned x0 + ldr x1, [tsk, #TI_FLAGS] // re-check for syscall tracing + and x2, x1, #_TIF_SYSCALL_WORK + cbnz x2, ret_fast_syscall_trace + and x2, x1, #_TIF_WORK_MASK +- cbnz x2, fast_work_pending ++ cbnz x2, work_pending + enable_step_tsk x1, x2 +- kernel_exit 0, ret = 1 ++ kernel_exit 0 + ret_fast_syscall_trace: + enable_irq // enable interrupts +- b __sys_trace_return ++ b __sys_trace_return_skipped // we already saved x0 + + /* + * Ok, we need to do extra processing, enter the slow path. + */ +-fast_work_pending: +- str x0, [sp, #S_X0] // returned x0 + work_pending: + tbnz x1, #TIF_NEED_RESCHED, work_resched + /* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */ +@@ -652,7 +647,7 @@ ret_to_user: + cbnz x2, work_pending + enable_step_tsk x1, x2 + no_work_pending: +- kernel_exit 0, ret = 0 ++ kernel_exit 0 + ENDPROC(ret_to_user) + + /* +diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c +index 44d6f7545505..c56956a16d3f 100644 +--- a/arch/arm64/kernel/fpsimd.c ++++ b/arch/arm64/kernel/fpsimd.c +@@ -158,6 +158,7 @@ void fpsimd_thread_switch(struct task_struct *next) + void fpsimd_flush_thread(void) + { + memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); ++ fpsimd_flush_task_state(current); + set_thread_flag(TIF_FOREIGN_FPSTATE); + } + +diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S +index c0ff3ce4299e..370541162658 100644 +--- a/arch/arm64/kernel/head.S ++++ b/arch/arm64/kernel/head.S +@@ -528,6 +528,11 @@ CPU_LE( movk x0, #0x30d0, lsl #16 ) // Clear EE and E0E on LE systems + msr hstr_el2, xzr // Disable CP15 traps to EL2 + #endif + ++ /* EL2 debug */ ++ mrs x0, pmcr_el0 // Disable debug access traps ++ ubfx x0, x0, #11, #5 // to EL2 and allow access to ++ msr mdcr_el2, x0 // all PMU counters from EL1 ++ + /* Stage-2 translation */ + msr vttbr_el2, xzr + +diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c +index 67bf4107f6ef..876eb8df50bf 100644 +--- a/arch/arm64/kernel/module.c ++++ b/arch/arm64/kernel/module.c +@@ -332,12 +332,14 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, + ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, + AARCH64_INSN_IMM_ADR); + break; ++#ifndef CONFIG_ARM64_ERRATUM_843419 + case R_AARCH64_ADR_PREL_PG_HI21_NC: + overflow_check = false; + case R_AARCH64_ADR_PREL_PG_HI21: + ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, + AARCH64_INSN_IMM_ADR); + break; ++#endif + case R_AARCH64_ADD_ABS_LO12_NC: + case R_AARCH64_LDST8_ABS_LO12_NC: + overflow_check = false; +diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c +index 948f0ad2de23..71ef6dc89ae5 100644 +--- a/arch/arm64/kernel/signal32.c ++++ b/arch/arm64/kernel/signal32.c +@@ -212,14 +212,32 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) + + /* + * VFP save/restore code. ++ * ++ * We have to be careful with endianness, since the fpsimd context-switch ++ * code operates on 128-bit (Q) register values whereas the compat ABI ++ * uses an array of 64-bit (D) registers. Consequently, we need to swap ++ * the two halves of each Q register when running on a big-endian CPU. + */ ++union __fpsimd_vreg { ++ __uint128_t raw; ++ struct { ++#ifdef __AARCH64EB__ ++ u64 hi; ++ u64 lo; ++#else ++ u64 lo; ++ u64 hi; ++#endif ++ }; ++}; ++ + static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame) + { + struct fpsimd_state *fpsimd = ¤t->thread.fpsimd_state; + compat_ulong_t magic = VFP_MAGIC; + compat_ulong_t size = VFP_STORAGE_SIZE; + compat_ulong_t fpscr, fpexc; +- int err = 0; ++ int i, err = 0; + + /* + * Save the hardware registers to the fpsimd_state structure. +@@ -235,10 +253,15 @@ static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame) + /* + * Now copy the FP registers. Since the registers are packed, + * we can copy the prefix we want (V0-V15) as it is. +- * FIXME: Won't work if big endian. + */ +- err |= __copy_to_user(&frame->ufp.fpregs, fpsimd->vregs, +- sizeof(frame->ufp.fpregs)); ++ for (i = 0; i < ARRAY_SIZE(frame->ufp.fpregs); i += 2) { ++ union __fpsimd_vreg vreg = { ++ .raw = fpsimd->vregs[i >> 1], ++ }; ++ ++ __put_user_error(vreg.lo, &frame->ufp.fpregs[i], err); ++ __put_user_error(vreg.hi, &frame->ufp.fpregs[i + 1], err); ++ } + + /* Create an AArch32 fpscr from the fpsr and the fpcr. */ + fpscr = (fpsimd->fpsr & VFP_FPSCR_STAT_MASK) | +@@ -263,7 +286,7 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) + compat_ulong_t magic = VFP_MAGIC; + compat_ulong_t size = VFP_STORAGE_SIZE; + compat_ulong_t fpscr; +- int err = 0; ++ int i, err = 0; + + __get_user_error(magic, &frame->magic, err); + __get_user_error(size, &frame->size, err); +@@ -273,12 +296,14 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame) + if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) + return -EINVAL; + +- /* +- * Copy the FP registers into the start of the fpsimd_state. +- * FIXME: Won't work if big endian. +- */ +- err |= __copy_from_user(fpsimd.vregs, frame->ufp.fpregs, +- sizeof(frame->ufp.fpregs)); ++ /* Copy the FP registers into the start of the fpsimd_state. */ ++ for (i = 0; i < ARRAY_SIZE(frame->ufp.fpregs); i += 2) { ++ union __fpsimd_vreg vreg; ++ ++ __get_user_error(vreg.lo, &frame->ufp.fpregs[i], err); ++ __get_user_error(vreg.hi, &frame->ufp.fpregs[i + 1], err); ++ fpsimd.vregs[i >> 1] = vreg.raw; ++ } + + /* Extract the fpsr and the fpcr from the fpscr */ + __get_user_error(fpscr, &frame->ufp.fpscr, err); +diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S +index 17a8fb14f428..3c6051cbf442 100644 +--- a/arch/arm64/kvm/hyp.S ++++ b/arch/arm64/kvm/hyp.S +@@ -840,8 +840,6 @@ + mrs x3, cntv_ctl_el0 + and x3, x3, #3 + str w3, [x0, #VCPU_TIMER_CNTV_CTL] +- bic x3, x3, #1 // Clear Enable +- msr cntv_ctl_el0, x3 + + isb + +@@ -849,6 +847,9 @@ + str x3, [x0, #VCPU_TIMER_CNTV_CVAL] + + 1: ++ // Disable the virtual timer ++ msr cntv_ctl_el0, xzr ++ + // Allow physical timer/counter access for the host + mrs x2, cnthctl_el2 + orr x2, x2, #3 +@@ -943,13 +944,15 @@ ENTRY(__kvm_vcpu_run) + // Guest context + add x2, x0, #VCPU_CONTEXT + ++ // We must restore the 32-bit state before the sysregs, thanks ++ // to Cortex-A57 erratum #852523. ++ restore_guest_32bit_state + bl __restore_sysregs + bl __restore_fpsimd + + skip_debug_state x3, 1f + bl __restore_debug + 1: +- restore_guest_32bit_state + restore_guest_regs + + // That's it, no more messing around. +diff --git a/arch/h8300/boot/compressed/misc.c b/arch/h8300/boot/compressed/misc.c +index 704274127c07..c4f2cfcb117b 100644 +--- a/arch/h8300/boot/compressed/misc.c ++++ b/arch/h8300/boot/compressed/misc.c +@@ -70,5 +70,5 @@ void decompress_kernel(void) + free_mem_ptr = (unsigned long)&_end; + free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; + +- decompress(input_data, input_len, NULL, NULL, output, NULL, error); ++ __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); + } +diff --git a/arch/m32r/boot/compressed/misc.c b/arch/m32r/boot/compressed/misc.c +index 28a09529f206..3a7692745868 100644 +--- a/arch/m32r/boot/compressed/misc.c ++++ b/arch/m32r/boot/compressed/misc.c +@@ -86,6 +86,7 @@ decompress_kernel(int mmu_on, unsigned char *zimage_data, + free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE; + + puts("\nDecompressing Linux... "); +- decompress(input_data, input_len, NULL, NULL, output_data, NULL, error); ++ __decompress(input_data, input_len, NULL, NULL, output_data, 0, ++ NULL, error); + puts("done.\nBooting the kernel.\n"); + } +diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c +index 54831069a206..080cd53bac36 100644 +--- a/arch/mips/boot/compressed/decompress.c ++++ b/arch/mips/boot/compressed/decompress.c +@@ -111,8 +111,8 @@ void decompress_kernel(unsigned long boot_heap_start) + puts("\n"); + + /* Decompress the kernel with according algorithm */ +- decompress((char *)zimage_start, zimage_size, 0, 0, +- (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, error); ++ __decompress((char *)zimage_start, zimage_size, 0, 0, ++ (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, 0, error); + + /* FIXME: should we flush cache here? */ + puts("Now, booting the kernel...\n"); +diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S +index 1b6ca634e646..9f71c06aebf6 100644 +--- a/arch/mips/kernel/cps-vec.S ++++ b/arch/mips/kernel/cps-vec.S +@@ -152,7 +152,7 @@ dcache_done: + + /* Enter the coherent domain */ + li t0, 0xff +- PTR_S t0, GCR_CL_COHERENCE_OFS(v1) ++ sw t0, GCR_CL_COHERENCE_OFS(v1) + ehb + + /* Jump to kseg0 */ +@@ -302,7 +302,7 @@ LEAF(mips_cps_boot_vpes) + PTR_L t0, 0(t0) + + /* Calculate a pointer to this cores struct core_boot_config */ +- PTR_L t0, GCR_CL_ID_OFS(t0) ++ lw t0, GCR_CL_ID_OFS(t0) + li t1, COREBOOTCFG_SIZE + mul t0, t0, t1 + PTR_LA t1, mips_cps_core_bootcfg +diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c +index 712f17a2ecf2..f0f1b98a5fde 100644 +--- a/arch/mips/math-emu/cp1emu.c ++++ b/arch/mips/math-emu/cp1emu.c +@@ -1137,7 +1137,7 @@ emul: + break; + + case mfhc_op: +- if (!cpu_has_mips_r2) ++ if (!cpu_has_mips_r2_r6) + goto sigill; + + /* copregister rd -> gpr[rt] */ +@@ -1148,7 +1148,7 @@ emul: + break; + + case mthc_op: +- if (!cpu_has_mips_r2) ++ if (!cpu_has_mips_r2_r6) + goto sigill; + + /* copregister rd <- gpr[rt] */ +@@ -1181,6 +1181,24 @@ emul: + } + break; + ++ case bc1eqz_op: ++ case bc1nez_op: ++ if (!cpu_has_mips_r6 || delay_slot(xcp)) ++ return SIGILL; ++ ++ cond = likely = 0; ++ switch (MIPSInst_RS(ir)) { ++ case bc1eqz_op: ++ if (get_fpr32(¤t->thread.fpu.fpr[MIPSInst_RT(ir)], 0) & 0x1) ++ cond = 1; ++ break; ++ case bc1nez_op: ++ if (!(get_fpr32(¤t->thread.fpu.fpr[MIPSInst_RT(ir)], 0) & 0x1)) ++ cond = 1; ++ break; ++ } ++ goto branch_common; ++ + case bc_op: + if (delay_slot(xcp)) + return SIGILL; +@@ -1207,7 +1225,7 @@ emul: + case bct_op: + break; + } +- ++branch_common: + set_delay_slot(xcp); + if (cond) { + /* +diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c +index f3191db6e2e9..c0eab24f6a9e 100644 +--- a/arch/parisc/kernel/irq.c ++++ b/arch/parisc/kernel/irq.c +@@ -507,8 +507,8 @@ void do_cpu_irq_mask(struct pt_regs *regs) + struct pt_regs *old_regs; + unsigned long eirr_val; + int irq, cpu = smp_processor_id(); +-#ifdef CONFIG_SMP + struct irq_desc *desc; ++#ifdef CONFIG_SMP + cpumask_t dest; + #endif + +@@ -521,8 +521,12 @@ void do_cpu_irq_mask(struct pt_regs *regs) + goto set_out; + irq = eirr_to_irq(eirr_val); + +-#ifdef CONFIG_SMP ++ /* Filter out spurious interrupts, mostly from serial port at bootup */ + desc = irq_to_desc(irq); ++ if (unlikely(!desc->action)) ++ goto set_out; ++ ++#ifdef CONFIG_SMP + cpumask_copy(&dest, desc->irq_data.affinity); + if (irqd_is_per_cpu(&desc->irq_data) && + !cpumask_test_cpu(smp_processor_id(), &dest)) { +diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S +index 7ef22e3387e0..0b8d26d3ba43 100644 +--- a/arch/parisc/kernel/syscall.S ++++ b/arch/parisc/kernel/syscall.S +@@ -821,7 +821,7 @@ cas2_action: + /* 64bit CAS */ + #ifdef CONFIG_64BIT + 19: ldd,ma 0(%sr3,%r26), %r29 +- sub,= %r29, %r25, %r0 ++ sub,*= %r29, %r25, %r0 + b,n cas2_end + 20: std,ma %r24, 0(%sr3,%r26) + copy %r0, %r28 +diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile +index 73eddda53b8e..4eec430d8fa8 100644 +--- a/arch/powerpc/boot/Makefile ++++ b/arch/powerpc/boot/Makefile +@@ -28,6 +28,9 @@ BOOTCFLAGS += -m64 + endif + ifdef CONFIG_CPU_BIG_ENDIAN + BOOTCFLAGS += -mbig-endian ++else ++BOOTCFLAGS += -mlittle-endian ++BOOTCFLAGS += $(call cc-option,-mabi=elfv2) + endif + + BOOTAFLAGS := -D__ASSEMBLY__ $(BOOTCFLAGS) -traditional -nostdinc +diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h +index 3bb7488bd24b..7ee2300ee392 100644 +--- a/arch/powerpc/include/asm/pgtable-ppc64.h ++++ b/arch/powerpc/include/asm/pgtable-ppc64.h +@@ -135,7 +135,19 @@ + #define pte_iterate_hashed_end() } while(0) + + #ifdef CONFIG_PPC_HAS_HASH_64K +-#define pte_pagesize_index(mm, addr, pte) get_slice_psize(mm, addr) ++/* ++ * We expect this to be called only for user addresses or kernel virtual ++ * addresses other than the linear mapping. ++ */ ++#define pte_pagesize_index(mm, addr, pte) \ ++ ({ \ ++ unsigned int psize; \ ++ if (is_kernel_addr(addr)) \ ++ psize = MMU_PAGE_4K; \ ++ else \ ++ psize = get_slice_psize(mm, addr); \ ++ psize; \ ++ }) + #else + #define pte_pagesize_index(mm, addr, pte) MMU_PAGE_4K + #endif +diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h +index 7a4ede16b283..b77ef369c0f0 100644 +--- a/arch/powerpc/include/asm/rtas.h ++++ b/arch/powerpc/include/asm/rtas.h +@@ -343,6 +343,7 @@ extern void rtas_power_off(void); + extern void rtas_halt(void); + extern void rtas_os_term(char *str); + extern int rtas_get_sensor(int sensor, int index, int *state); ++extern int rtas_get_sensor_fast(int sensor, int index, int *state); + extern int rtas_get_power_level(int powerdomain, int *level); + extern int rtas_set_power_level(int powerdomain, int level, int *setlevel); + extern bool rtas_indicator_present(int token, int *maxindex); +diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h +index 58abeda64cb7..15cca17cba4b 100644 +--- a/arch/powerpc/include/asm/switch_to.h ++++ b/arch/powerpc/include/asm/switch_to.h +@@ -29,6 +29,7 @@ static inline void save_early_sprs(struct thread_struct *prev) {} + + extern void enable_kernel_fp(void); + extern void enable_kernel_altivec(void); ++extern void enable_kernel_vsx(void); + extern int emulate_altivec(struct pt_regs *); + extern void __giveup_vsx(struct task_struct *); + extern void giveup_vsx(struct task_struct *); +diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c +index af9b597b10af..01c961d5d2de 100644 +--- a/arch/powerpc/kernel/eeh.c ++++ b/arch/powerpc/kernel/eeh.c +@@ -308,11 +308,26 @@ void eeh_slot_error_detail(struct eeh_pe *pe, int severity) + if (!(pe->type & EEH_PE_PHB)) { + if (eeh_has_flag(EEH_ENABLE_IO_FOR_LOG)) + eeh_pci_enable(pe, EEH_OPT_THAW_MMIO); ++ ++ /* ++ * The config space of some PCI devices can't be accessed ++ * when their PEs are in frozen state. Otherwise, fenced ++ * PHB might be seen. Those PEs are identified with flag ++ * EEH_PE_CFG_RESTRICTED, indicating EEH_PE_CFG_BLOCKED ++ * is set automatically when the PE is put to EEH_PE_ISOLATED. ++ * ++ * Restoring BARs possibly triggers PCI config access in ++ * (OPAL) firmware and then causes fenced PHB. If the ++ * PCI config is blocked with flag EEH_PE_CFG_BLOCKED, it's ++ * pointless to restore BARs and dump config space. ++ */ + eeh_ops->configure_bridge(pe); +- eeh_pe_restore_bars(pe); ++ if (!(pe->state & EEH_PE_CFG_BLOCKED)) { ++ eeh_pe_restore_bars(pe); + +- pci_regs_buf[0] = 0; +- eeh_pe_traverse(pe, eeh_dump_pe_log, &loglen); ++ pci_regs_buf[0] = 0; ++ eeh_pe_traverse(pe, eeh_dump_pe_log, &loglen); ++ } + } + + eeh_ops->get_log(pe, severity, pci_regs_buf, loglen); +@@ -1116,9 +1131,6 @@ void eeh_add_device_late(struct pci_dev *dev) + return; + } + +- if (eeh_has_flag(EEH_PROBE_MODE_DEV)) +- eeh_ops->probe(pdn, NULL); +- + /* + * The EEH cache might not be removed correctly because of + * unbalanced kref to the device during unplug time, which +@@ -1142,6 +1154,9 @@ void eeh_add_device_late(struct pci_dev *dev) + dev->dev.archdata.edev = NULL; + } + ++ if (eeh_has_flag(EEH_PROBE_MODE_DEV)) ++ eeh_ops->probe(pdn, NULL); ++ + edev->pdev = dev; + dev->dev.archdata.edev = edev; + +diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c +index 8005e18d1b40..64e6e9d9e656 100644 +--- a/arch/powerpc/kernel/process.c ++++ b/arch/powerpc/kernel/process.c +@@ -204,8 +204,6 @@ EXPORT_SYMBOL_GPL(flush_altivec_to_thread); + #endif /* CONFIG_ALTIVEC */ + + #ifdef CONFIG_VSX +-#if 0 +-/* not currently used, but some crazy RAID module might want to later */ + void enable_kernel_vsx(void) + { + WARN_ON(preemptible()); +@@ -220,7 +218,6 @@ void enable_kernel_vsx(void) + #endif /* CONFIG_SMP */ + } + EXPORT_SYMBOL(enable_kernel_vsx); +-#endif + + void giveup_vsx(struct task_struct *tsk) + { +diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c +index 7a488c108410..caffb10e7aa3 100644 +--- a/arch/powerpc/kernel/rtas.c ++++ b/arch/powerpc/kernel/rtas.c +@@ -584,6 +584,23 @@ int rtas_get_sensor(int sensor, int index, int *state) + } + EXPORT_SYMBOL(rtas_get_sensor); + ++int rtas_get_sensor_fast(int sensor, int index, int *state) ++{ ++ int token = rtas_token("get-sensor-state"); ++ int rc; ++ ++ if (token == RTAS_UNKNOWN_SERVICE) ++ return -ENOENT; ++ ++ rc = rtas_call(token, 2, 2, state, sensor, index); ++ WARN_ON(rc == RTAS_BUSY || (rc >= RTAS_EXTENDED_DELAY_MIN && ++ rc <= RTAS_EXTENDED_DELAY_MAX)); ++ ++ if (rc < 0) ++ return rtas_error_rc(rc); ++ return rc; ++} ++ + bool rtas_indicator_present(int token, int *maxindex) + { + int proplen, count, i; +diff --git a/arch/powerpc/mm/hugepage-hash64.c b/arch/powerpc/mm/hugepage-hash64.c +index 43dafb9d6a46..4d87122cf6a7 100644 +--- a/arch/powerpc/mm/hugepage-hash64.c ++++ b/arch/powerpc/mm/hugepage-hash64.c +@@ -85,7 +85,6 @@ int __hash_page_thp(unsigned long ea, unsigned long access, unsigned long vsid, + BUG_ON(index >= 4096); + + vpn = hpt_vpn(ea, vsid, ssize); +- hash = hpt_hash(vpn, shift, ssize); + hpte_slot_array = get_hpte_slot_array(pmdp); + if (psize == MMU_PAGE_4K) { + /* +@@ -101,6 +100,7 @@ int __hash_page_thp(unsigned long ea, unsigned long access, unsigned long vsid, + valid = hpte_valid(hpte_slot_array, index); + if (valid) { + /* update the hpte bits */ ++ hash = hpt_hash(vpn, shift, ssize); + hidx = hpte_hash_index(hpte_slot_array, index); + if (hidx & _PTEIDX_SECONDARY) + hash = ~hash; +@@ -126,6 +126,7 @@ int __hash_page_thp(unsigned long ea, unsigned long access, unsigned long vsid, + if (!valid) { + unsigned long hpte_group; + ++ hash = hpt_hash(vpn, shift, ssize); + /* insert new entry */ + pa = pmd_pfn(__pmd(old_pmd)) << PAGE_SHIFT; + new_pmd |= _PAGE_HASHPTE; +diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c +index 85cbc96eff6c..8b64f89e68c9 100644 +--- a/arch/powerpc/platforms/powernv/pci-ioda.c ++++ b/arch/powerpc/platforms/powernv/pci-ioda.c +@@ -2078,9 +2078,23 @@ static long pnv_pci_ioda2_setup_default_config(struct pnv_ioda_pe *pe) + struct iommu_table *tbl = NULL; + long rc; + ++ /* ++ * crashkernel= specifies the kdump kernel's maximum memory at ++ * some offset and there is no guaranteed the result is a power ++ * of 2, which will cause errors later. ++ */ ++ const u64 max_memory = __rounddown_pow_of_two(memory_hotplug_max()); ++ ++ /* ++ * In memory constrained environments, e.g. kdump kernel, the ++ * DMA window can be larger than available memory, which will ++ * cause errors later. ++ */ ++ const u64 window_size = min((u64)pe->table_group.tce32_size, max_memory); ++ + rc = pnv_pci_ioda2_create_table(&pe->table_group, 0, + IOMMU_PAGE_SHIFT_4K, +- pe->table_group.tce32_size, ++ window_size, + POWERNV_IOMMU_DEFAULT_LEVELS, &tbl); + if (rc) { + pe_err(pe, "Failed to create 32-bit TCE table, err %ld", +diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c +index 47d9cebe7159..db17827eb746 100644 +--- a/arch/powerpc/platforms/pseries/dlpar.c ++++ b/arch/powerpc/platforms/pseries/dlpar.c +@@ -422,8 +422,10 @@ static ssize_t dlpar_cpu_probe(const char *buf, size_t count) + + dn = dlpar_configure_connector(cpu_to_be32(drc_index), parent); + of_node_put(parent); +- if (!dn) ++ if (!dn) { ++ dlpar_release_drc(drc_index); + return -EINVAL; ++ } + + rc = dlpar_attach_node(dn); + if (rc) { +diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c +index 02e4a1745516..3b6647e574b6 100644 +--- a/arch/powerpc/platforms/pseries/ras.c ++++ b/arch/powerpc/platforms/pseries/ras.c +@@ -189,7 +189,8 @@ static irqreturn_t ras_epow_interrupt(int irq, void *dev_id) + int state; + int critical; + +- status = rtas_get_sensor(EPOW_SENSOR_TOKEN, EPOW_SENSOR_INDEX, &state); ++ status = rtas_get_sensor_fast(EPOW_SENSOR_TOKEN, EPOW_SENSOR_INDEX, ++ &state); + + if (state > 3) + critical = 1; /* Time Critical */ +diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c +index df6a7041922b..e6e8b241d717 100644 +--- a/arch/powerpc/platforms/pseries/setup.c ++++ b/arch/powerpc/platforms/pseries/setup.c +@@ -268,6 +268,11 @@ static int pci_dn_reconfig_notifier(struct notifier_block *nb, unsigned long act + eeh_dev_init(PCI_DN(np), pci->phb); + } + break; ++ case OF_RECONFIG_DETACH_NODE: ++ pci = PCI_DN(np); ++ if (pci) ++ list_del(&pci->list); ++ break; + default: + err = NOTIFY_DONE; + break; +diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c +index 42506b371b74..4da604ebf6fd 100644 +--- a/arch/s390/boot/compressed/misc.c ++++ b/arch/s390/boot/compressed/misc.c +@@ -167,7 +167,7 @@ unsigned long decompress_kernel(void) + #endif + + puts("Uncompressing Linux... "); +- decompress(input_data, input_len, NULL, NULL, output, NULL, error); ++ __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); + puts("Ok, booting the kernel.\n"); + return (unsigned long) output; + } +diff --git a/arch/sh/boot/compressed/misc.c b/arch/sh/boot/compressed/misc.c +index 95470a472d2c..208a9753ab38 100644 +--- a/arch/sh/boot/compressed/misc.c ++++ b/arch/sh/boot/compressed/misc.c +@@ -132,7 +132,7 @@ void decompress_kernel(void) + + puts("Uncompressing Linux... "); + cache_control(CACHE_ENABLE); +- decompress(input_data, input_len, NULL, NULL, output, NULL, error); ++ __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); + cache_control(CACHE_DISABLE); + puts("Ok, booting the kernel.\n"); + } +diff --git a/arch/unicore32/boot/compressed/misc.c b/arch/unicore32/boot/compressed/misc.c +index 176d5bda3559..5c65dfee278c 100644 +--- a/arch/unicore32/boot/compressed/misc.c ++++ b/arch/unicore32/boot/compressed/misc.c +@@ -119,8 +119,8 @@ unsigned long decompress_kernel(unsigned long output_start, + output_ptr = get_unaligned_le32(tmp); + + arch_decomp_puts("Uncompressing Linux..."); +- decompress(input_data, input_data_end - input_data, NULL, NULL, +- output_data, NULL, error); ++ __decompress(input_data, input_data_end - input_data, NULL, NULL, ++ output_data, 0, NULL, error); + arch_decomp_puts(" done, booting the kernel.\n"); + return output_ptr; + } +diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c +index a107b935e22f..e28437e0f708 100644 +--- a/arch/x86/boot/compressed/misc.c ++++ b/arch/x86/boot/compressed/misc.c +@@ -424,7 +424,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap, + #endif + + debug_putstr("\nDecompressing Linux... "); +- decompress(input_data, input_len, NULL, NULL, output, NULL, error); ++ __decompress(input_data, input_len, NULL, NULL, output, output_len, ++ NULL, error); + parse_elf(output); + /* + * 32-bit always performs relocations. 64-bit relocations are only +diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c +index 8340e45c891a..68aec42545c2 100644 +--- a/arch/x86/mm/init_32.c ++++ b/arch/x86/mm/init_32.c +@@ -137,6 +137,7 @@ page_table_range_init_count(unsigned long start, unsigned long end) + + vaddr = start; + pgd_idx = pgd_index(vaddr); ++ pmd_idx = pmd_index(vaddr); + + for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd_idx++) { + for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end); +diff --git a/block/blk-flush.c b/block/blk-flush.c +index 20badd7b9d1b..9c423e53324a 100644 +--- a/block/blk-flush.c ++++ b/block/blk-flush.c +@@ -73,6 +73,7 @@ + + #include "blk.h" + #include "blk-mq.h" ++#include "blk-mq-tag.h" + + /* FLUSH/FUA sequences */ + enum { +@@ -226,7 +227,12 @@ static void flush_end_io(struct request *flush_rq, int error) + struct blk_flush_queue *fq = blk_get_flush_queue(q, flush_rq->mq_ctx); + + if (q->mq_ops) { ++ struct blk_mq_hw_ctx *hctx; ++ ++ /* release the tag's ownership to the req cloned from */ + spin_lock_irqsave(&fq->mq_flush_lock, flags); ++ hctx = q->mq_ops->map_queue(q, flush_rq->mq_ctx->cpu); ++ blk_mq_tag_set_rq(hctx, flush_rq->tag, fq->orig_rq); + flush_rq->tag = -1; + } + +@@ -308,11 +314,18 @@ static bool blk_kick_flush(struct request_queue *q, struct blk_flush_queue *fq) + + /* + * Borrow tag from the first request since they can't +- * be in flight at the same time. ++ * be in flight at the same time. And acquire the tag's ++ * ownership for flush req. + */ + if (q->mq_ops) { ++ struct blk_mq_hw_ctx *hctx; ++ + flush_rq->mq_ctx = first_rq->mq_ctx; + flush_rq->tag = first_rq->tag; ++ fq->orig_rq = first_rq; ++ ++ hctx = q->mq_ops->map_queue(q, first_rq->mq_ctx->cpu); ++ blk_mq_tag_set_rq(hctx, first_rq->tag, flush_rq); + } + + flush_rq->cmd_type = REQ_TYPE_FS; +diff --git a/block/blk-mq-sysfs.c b/block/blk-mq-sysfs.c +index b79685e06b70..279c5d674edf 100644 +--- a/block/blk-mq-sysfs.c ++++ b/block/blk-mq-sysfs.c +@@ -141,15 +141,26 @@ static ssize_t blk_mq_sysfs_completed_show(struct blk_mq_ctx *ctx, char *page) + + static ssize_t sysfs_list_show(char *page, struct list_head *list, char *msg) + { +- char *start_page = page; + struct request *rq; ++ int len = snprintf(page, PAGE_SIZE - 1, "%s:\n", msg); ++ ++ list_for_each_entry(rq, list, queuelist) { ++ const int rq_len = 2 * sizeof(rq) + 2; ++ ++ /* if the output will be truncated */ ++ if (PAGE_SIZE - 1 < len + rq_len) { ++ /* backspacing if it can't hold '\t...\n' */ ++ if (PAGE_SIZE - 1 < len + 5) ++ len -= rq_len; ++ len += snprintf(page + len, PAGE_SIZE - 1 - len, ++ "\t...\n"); ++ break; ++ } ++ len += snprintf(page + len, PAGE_SIZE - 1 - len, ++ "\t%p\n", rq); ++ } + +- page += sprintf(page, "%s:\n", msg); +- +- list_for_each_entry(rq, list, queuelist) +- page += sprintf(page, "\t%p\n", rq); +- +- return page - start_page; ++ return len; + } + + static ssize_t blk_mq_sysfs_rq_list_show(struct blk_mq_ctx *ctx, char *page) +diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c +index 9b6e28830b82..9115c6d59948 100644 +--- a/block/blk-mq-tag.c ++++ b/block/blk-mq-tag.c +@@ -429,7 +429,7 @@ static void bt_for_each(struct blk_mq_hw_ctx *hctx, + for (bit = find_first_bit(&bm->word, bm->depth); + bit < bm->depth; + bit = find_next_bit(&bm->word, bm->depth, bit + 1)) { +- rq = blk_mq_tag_to_rq(hctx->tags, off + bit); ++ rq = hctx->tags->rqs[off + bit]; + if (rq->q == hctx->queue) + fn(hctx, rq, data, reserved); + } +@@ -453,7 +453,7 @@ static void bt_tags_for_each(struct blk_mq_tags *tags, + for (bit = find_first_bit(&bm->word, bm->depth); + bit < bm->depth; + bit = find_next_bit(&bm->word, bm->depth, bit + 1)) { +- rq = blk_mq_tag_to_rq(tags, off + bit); ++ rq = tags->rqs[off + bit]; + fn(rq, data, reserved); + } + +diff --git a/block/blk-mq-tag.h b/block/blk-mq-tag.h +index 75893a34237d..9eb2cf4f01cb 100644 +--- a/block/blk-mq-tag.h ++++ b/block/blk-mq-tag.h +@@ -89,4 +89,16 @@ static inline void blk_mq_tag_idle(struct blk_mq_hw_ctx *hctx) + __blk_mq_tag_idle(hctx); + } + ++/* ++ * This helper should only be used for flush request to share tag ++ * with the request cloned from, and both the two requests can't be ++ * in flight at the same time. The caller has to make sure the tag ++ * can't be freed. ++ */ ++static inline void blk_mq_tag_set_rq(struct blk_mq_hw_ctx *hctx, ++ unsigned int tag, struct request *rq) ++{ ++ hctx->tags->rqs[tag] = rq; ++} ++ + #endif +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 7d842db59699..176262ec3731 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -559,23 +559,9 @@ void blk_mq_abort_requeue_list(struct request_queue *q) + } + EXPORT_SYMBOL(blk_mq_abort_requeue_list); + +-static inline bool is_flush_request(struct request *rq, +- struct blk_flush_queue *fq, unsigned int tag) +-{ +- return ((rq->cmd_flags & REQ_FLUSH_SEQ) && +- fq->flush_rq->tag == tag); +-} +- + struct request *blk_mq_tag_to_rq(struct blk_mq_tags *tags, unsigned int tag) + { +- struct request *rq = tags->rqs[tag]; +- /* mq_ctx of flush rq is always cloned from the corresponding req */ +- struct blk_flush_queue *fq = blk_get_flush_queue(rq->q, rq->mq_ctx); +- +- if (!is_flush_request(rq, fq, tag)) +- return rq; +- +- return fq->flush_rq; ++ return tags->rqs[tag]; + } + EXPORT_SYMBOL(blk_mq_tag_to_rq); + +diff --git a/block/blk.h b/block/blk.h +index 026d9594142b..838188b35a83 100644 +--- a/block/blk.h ++++ b/block/blk.h +@@ -22,6 +22,12 @@ struct blk_flush_queue { + struct list_head flush_queue[2]; + struct list_head flush_data_in_flight; + struct request *flush_rq; ++ ++ /* ++ * flush_rq shares tag with this rq, both can't be active ++ * at the same time ++ */ ++ struct request *orig_rq; + spinlock_t mq_flush_lock; + }; + +diff --git a/drivers/base/node.c b/drivers/base/node.c +index 31df474d72f4..560751bad294 100644 +--- a/drivers/base/node.c ++++ b/drivers/base/node.c +@@ -392,6 +392,16 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid) + for (pfn = sect_start_pfn; pfn <= sect_end_pfn; pfn++) { + int page_nid; + ++ /* ++ * memory block could have several absent sections from start. ++ * skip pfn range from absent section ++ */ ++ if (!pfn_present(pfn)) { ++ pfn = round_down(pfn + PAGES_PER_SECTION, ++ PAGES_PER_SECTION) - 1; ++ continue; ++ } ++ + page_nid = get_nid_for_pfn(pfn); + if (page_nid < 0) + continue; +diff --git a/drivers/crypto/vmx/aes.c b/drivers/crypto/vmx/aes.c +index e79e567e43aa..263af709e536 100644 +--- a/drivers/crypto/vmx/aes.c ++++ b/drivers/crypto/vmx/aes.c +@@ -84,6 +84,7 @@ static int p8_aes_setkey(struct crypto_tfm *tfm, const u8 *key, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); + ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key); + pagefault_enable(); +@@ -103,6 +104,7 @@ static void p8_aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + aes_p8_encrypt(src, dst, &ctx->enc_key); + pagefault_enable(); + preempt_enable(); +@@ -119,6 +121,7 @@ static void p8_aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + aes_p8_decrypt(src, dst, &ctx->dec_key); + pagefault_enable(); + preempt_enable(); +diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c +index 7299995c78ec..0b8fe2ec5315 100644 +--- a/drivers/crypto/vmx/aes_cbc.c ++++ b/drivers/crypto/vmx/aes_cbc.c +@@ -85,6 +85,7 @@ static int p8_aes_cbc_setkey(struct crypto_tfm *tfm, const u8 *key, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); + ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key); + pagefault_enable(); +@@ -115,6 +116,7 @@ static int p8_aes_cbc_encrypt(struct blkcipher_desc *desc, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + + blkcipher_walk_init(&walk, dst, src, nbytes); + ret = blkcipher_walk_virt(desc, &walk); +@@ -155,6 +157,7 @@ static int p8_aes_cbc_decrypt(struct blkcipher_desc *desc, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + + blkcipher_walk_init(&walk, dst, src, nbytes); + ret = blkcipher_walk_virt(desc, &walk); +diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c +index ed3838781b4c..ee1306cd8f59 100644 +--- a/drivers/crypto/vmx/aes_ctr.c ++++ b/drivers/crypto/vmx/aes_ctr.c +@@ -82,6 +82,7 @@ static int p8_aes_ctr_setkey(struct crypto_tfm *tfm, const u8 *key, + + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); + pagefault_enable(); + +@@ -100,6 +101,7 @@ static void p8_aes_ctr_final(struct p8_aes_ctr_ctx *ctx, + + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key); + pagefault_enable(); + +@@ -132,6 +134,7 @@ static int p8_aes_ctr_crypt(struct blkcipher_desc *desc, + while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) { + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr, + walk.dst.virt.addr, + (nbytes & +diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c +index b5e29002b666..2183a2e77641 100644 +--- a/drivers/crypto/vmx/ghash.c ++++ b/drivers/crypto/vmx/ghash.c +@@ -119,6 +119,7 @@ static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + enable_kernel_fp(); + gcm_init_p8(ctx->htable, (const u64 *) key); + pagefault_enable(); +@@ -149,6 +150,7 @@ static int p8_ghash_update(struct shash_desc *desc, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, + dctx->buffer, GHASH_DIGEST_SIZE); +@@ -163,6 +165,7 @@ static int p8_ghash_update(struct shash_desc *desc, + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, src, len); + pagefault_enable(); +@@ -193,6 +196,7 @@ static int p8_ghash_final(struct shash_desc *desc, u8 *out) + preempt_disable(); + pagefault_disable(); + enable_kernel_altivec(); ++ enable_kernel_vsx(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, + dctx->buffer, GHASH_DIGEST_SIZE); +diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c +index cacb07b7a8f1..32e7b4a686ef 100644 +--- a/drivers/gpu/drm/i915/intel_ddi.c ++++ b/drivers/gpu/drm/i915/intel_ddi.c +@@ -1293,17 +1293,14 @@ skl_ddi_pll_select(struct intel_crtc *intel_crtc, + DPLL_CFGCR2_PDIV(wrpll_params.pdiv) | + wrpll_params.central_freq; + } else if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { +- struct drm_encoder *encoder = &intel_encoder->base; +- struct intel_dp *intel_dp = enc_to_intel_dp(encoder); +- +- switch (intel_dp->link_bw) { +- case DP_LINK_BW_1_62: ++ switch (crtc_state->port_clock / 2) { ++ case 81000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0); + break; +- case DP_LINK_BW_2_7: ++ case 135000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0); + break; +- case DP_LINK_BW_5_4: ++ case 270000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0); + break; + } +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index bd8f8863eb0e..ca2d923101fc 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -48,28 +48,28 @@ + #define INTEL_DP_RESOLUTION_FAILSAFE (3 << INTEL_DP_RESOLUTION_SHIFT_MASK) + + struct dp_link_dpll { +- int link_bw; ++ int clock; + struct dpll dpll; + }; + + static const struct dp_link_dpll gen4_dpll[] = { +- { DP_LINK_BW_1_62, ++ { 162000, + { .p1 = 2, .p2 = 10, .n = 2, .m1 = 23, .m2 = 8 } }, +- { DP_LINK_BW_2_7, ++ { 270000, + { .p1 = 1, .p2 = 10, .n = 1, .m1 = 14, .m2 = 2 } } + }; + + static const struct dp_link_dpll pch_dpll[] = { +- { DP_LINK_BW_1_62, ++ { 162000, + { .p1 = 2, .p2 = 10, .n = 1, .m1 = 12, .m2 = 9 } }, +- { DP_LINK_BW_2_7, ++ { 270000, + { .p1 = 1, .p2 = 10, .n = 2, .m1 = 14, .m2 = 8 } } + }; + + static const struct dp_link_dpll vlv_dpll[] = { +- { DP_LINK_BW_1_62, ++ { 162000, + { .p1 = 3, .p2 = 2, .n = 5, .m1 = 3, .m2 = 81 } }, +- { DP_LINK_BW_2_7, ++ { 270000, + { .p1 = 2, .p2 = 2, .n = 1, .m1 = 2, .m2 = 27 } } + }; + +@@ -83,11 +83,11 @@ static const struct dp_link_dpll chv_dpll[] = { + * m2 is stored in fixed point format using formula below + * (m2_int << 22) | m2_fraction + */ +- { DP_LINK_BW_1_62, /* m2_int = 32, m2_fraction = 1677722 */ ++ { 162000, /* m2_int = 32, m2_fraction = 1677722 */ + { .p1 = 4, .p2 = 2, .n = 1, .m1 = 2, .m2 = 0x819999a } }, +- { DP_LINK_BW_2_7, /* m2_int = 27, m2_fraction = 0 */ ++ { 270000, /* m2_int = 27, m2_fraction = 0 */ + { .p1 = 4, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } }, +- { DP_LINK_BW_5_4, /* m2_int = 27, m2_fraction = 0 */ ++ { 540000, /* m2_int = 27, m2_fraction = 0 */ + { .p1 = 2, .p2 = 1, .n = 1, .m1 = 2, .m2 = 0x6c00000 } } + }; + +@@ -1089,7 +1089,7 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector) + } + + static void +-skl_edp_set_pll_config(struct intel_crtc_state *pipe_config, int link_clock) ++skl_edp_set_pll_config(struct intel_crtc_state *pipe_config) + { + u32 ctrl1; + +@@ -1101,7 +1101,7 @@ skl_edp_set_pll_config(struct intel_crtc_state *pipe_config, int link_clock) + pipe_config->dpll_hw_state.cfgcr2 = 0; + + ctrl1 = DPLL_CTRL1_OVERRIDE(SKL_DPLL0); +- switch (link_clock / 2) { ++ switch (pipe_config->port_clock / 2) { + case 81000: + ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, + SKL_DPLL0); +@@ -1134,20 +1134,20 @@ skl_edp_set_pll_config(struct intel_crtc_state *pipe_config, int link_clock) + pipe_config->dpll_hw_state.ctrl1 = ctrl1; + } + +-static void +-hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config, int link_bw) ++void ++hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config) + { + memset(&pipe_config->dpll_hw_state, 0, + sizeof(pipe_config->dpll_hw_state)); + +- switch (link_bw) { +- case DP_LINK_BW_1_62: ++ switch (pipe_config->port_clock / 2) { ++ case 81000: + pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810; + break; +- case DP_LINK_BW_2_7: ++ case 135000: + pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350; + break; +- case DP_LINK_BW_5_4: ++ case 270000: + pipe_config->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700; + break; + } +@@ -1198,7 +1198,7 @@ intel_dp_source_rates(struct drm_device *dev, const int **source_rates) + + static void + intel_dp_set_clock(struct intel_encoder *encoder, +- struct intel_crtc_state *pipe_config, int link_bw) ++ struct intel_crtc_state *pipe_config) + { + struct drm_device *dev = encoder->base.dev; + const struct dp_link_dpll *divisor = NULL; +@@ -1220,7 +1220,7 @@ intel_dp_set_clock(struct intel_encoder *encoder, + + if (divisor && count) { + for (i = 0; i < count; i++) { +- if (link_bw == divisor[i].link_bw) { ++ if (pipe_config->port_clock == divisor[i].clock) { + pipe_config->dpll = divisor[i].dpll; + pipe_config->clock_set = true; + break; +@@ -1494,13 +1494,13 @@ found: + } + + if (IS_SKYLAKE(dev) && is_edp(intel_dp)) +- skl_edp_set_pll_config(pipe_config, common_rates[clock]); ++ skl_edp_set_pll_config(pipe_config); + else if (IS_BROXTON(dev)) + /* handled in ddi */; + else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) +- hsw_dp_set_ddi_pll_sel(pipe_config, intel_dp->link_bw); ++ hsw_dp_set_ddi_pll_sel(pipe_config); + else +- intel_dp_set_clock(encoder, pipe_config, intel_dp->link_bw); ++ intel_dp_set_clock(encoder, pipe_config); + + return true; + } +diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c +index 600afdbef8c9..8c127201ab3c 100644 +--- a/drivers/gpu/drm/i915/intel_dp_mst.c ++++ b/drivers/gpu/drm/i915/intel_dp_mst.c +@@ -33,6 +33,7 @@ + static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, + struct intel_crtc_state *pipe_config) + { ++ struct drm_device *dev = encoder->base.dev; + struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base); + struct intel_digital_port *intel_dig_port = intel_mst->primary; + struct intel_dp *intel_dp = &intel_dig_port->dp; +@@ -97,6 +98,10 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder, + &pipe_config->dp_m_n); + + pipe_config->dp_m_n.tu = slots; ++ ++ if (IS_HASWELL(dev) || IS_BROADWELL(dev)) ++ hsw_dp_set_ddi_pll_sel(pipe_config); ++ + return true; + + } +diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h +index 105928382e21..04d426156bdb 100644 +--- a/drivers/gpu/drm/i915/intel_drv.h ++++ b/drivers/gpu/drm/i915/intel_drv.h +@@ -1194,6 +1194,7 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp); + void intel_edp_drrs_invalidate(struct drm_device *dev, + unsigned frontbuffer_bits); + void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits); ++void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config); + + /* intel_dp_mst.c */ + int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id); +diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c +index c097d3a82bda..a9b01bcf7d0a 100644 +--- a/drivers/gpu/drm/radeon/radeon_combios.c ++++ b/drivers/gpu/drm/radeon/radeon_combios.c +@@ -3387,6 +3387,14 @@ void radeon_combios_asic_init(struct drm_device *dev) + rdev->pdev->subsystem_device == 0x30ae) + return; + ++ /* quirk for rs4xx HP Compaq dc5750 Small Form Factor to make it resume ++ * - it hangs on resume inside the dynclk 1 table. ++ */ ++ if (rdev->family == CHIP_RS480 && ++ rdev->pdev->subsystem_vendor == 0x103c && ++ rdev->pdev->subsystem_device == 0x280a) ++ return; ++ + /* DYN CLK 1 */ + table = combios_get_table_offset(dev, COMBIOS_DYN_CLK_1_TABLE); + if (table) +diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c +index 1c9cb65ac4cf..4233f5695352 100644 +--- a/drivers/i2c/busses/i2c-xgene-slimpro.c ++++ b/drivers/i2c/busses/i2c-xgene-slimpro.c +@@ -198,10 +198,10 @@ static int slimpro_i2c_blkrd(struct slimpro_i2c_dev *ctx, u32 chip, u32 addr, + int rc; + + paddr = dma_map_single(ctx->dev, ctx->dma_buffer, readlen, DMA_FROM_DEVICE); +- rc = dma_mapping_error(ctx->dev, paddr); +- if (rc) { ++ if (dma_mapping_error(ctx->dev, paddr)) { + dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", + ctx->dma_buffer); ++ rc = -ENOMEM; + goto err; + } + +@@ -241,10 +241,10 @@ static int slimpro_i2c_blkwr(struct slimpro_i2c_dev *ctx, u32 chip, + memcpy(ctx->dma_buffer, data, writelen); + paddr = dma_map_single(ctx->dev, ctx->dma_buffer, writelen, + DMA_TO_DEVICE); +- rc = dma_mapping_error(ctx->dev, paddr); +- if (rc) { ++ if (dma_mapping_error(ctx->dev, paddr)) { + dev_err(&ctx->adapter.dev, "Error in mapping dma buffer %p\n", + ctx->dma_buffer); ++ rc = -ENOMEM; + goto err; + } + +diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h +index ba365b6d1e8d..65cbfcc92f11 100644 +--- a/drivers/infiniband/core/uverbs.h ++++ b/drivers/infiniband/core/uverbs.h +@@ -85,7 +85,7 @@ + */ + + struct ib_uverbs_device { +- struct kref ref; ++ atomic_t refcount; + int num_comp_vectors; + struct completion comp; + struct device *dev; +@@ -94,6 +94,7 @@ struct ib_uverbs_device { + struct cdev cdev; + struct rb_root xrcd_tree; + struct mutex xrcd_tree_mutex; ++ struct kobject kobj; + }; + + struct ib_uverbs_event_file { +diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c +index bbb02ffe87df..a6ca83b3153f 100644 +--- a/drivers/infiniband/core/uverbs_cmd.c ++++ b/drivers/infiniband/core/uverbs_cmd.c +@@ -2346,6 +2346,12 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file, + next->send_flags = user_wr->send_flags; + + if (is_ud) { ++ if (next->opcode != IB_WR_SEND && ++ next->opcode != IB_WR_SEND_WITH_IMM) { ++ ret = -EINVAL; ++ goto out_put; ++ } ++ + next->wr.ud.ah = idr_read_ah(user_wr->wr.ud.ah, + file->ucontext); + if (!next->wr.ud.ah) { +@@ -2385,9 +2391,11 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file, + user_wr->wr.atomic.compare_add; + next->wr.atomic.swap = user_wr->wr.atomic.swap; + next->wr.atomic.rkey = user_wr->wr.atomic.rkey; ++ case IB_WR_SEND: + break; + default: +- break; ++ ret = -EINVAL; ++ goto out_put; + } + } + +diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c +index f6eef2da7097..15f4126a577d 100644 +--- a/drivers/infiniband/core/uverbs_main.c ++++ b/drivers/infiniband/core/uverbs_main.c +@@ -130,14 +130,18 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file, + static void ib_uverbs_add_one(struct ib_device *device); + static void ib_uverbs_remove_one(struct ib_device *device); + +-static void ib_uverbs_release_dev(struct kref *ref) ++static void ib_uverbs_release_dev(struct kobject *kobj) + { + struct ib_uverbs_device *dev = +- container_of(ref, struct ib_uverbs_device, ref); ++ container_of(kobj, struct ib_uverbs_device, kobj); + +- complete(&dev->comp); ++ kfree(dev); + } + ++static struct kobj_type ib_uverbs_dev_ktype = { ++ .release = ib_uverbs_release_dev, ++}; ++ + static void ib_uverbs_release_event_file(struct kref *ref) + { + struct ib_uverbs_event_file *file = +@@ -303,13 +307,19 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file, + return context->device->dealloc_ucontext(context); + } + ++static void ib_uverbs_comp_dev(struct ib_uverbs_device *dev) ++{ ++ complete(&dev->comp); ++} ++ + static void ib_uverbs_release_file(struct kref *ref) + { + struct ib_uverbs_file *file = + container_of(ref, struct ib_uverbs_file, ref); + + module_put(file->device->ib_dev->owner); +- kref_put(&file->device->ref, ib_uverbs_release_dev); ++ if (atomic_dec_and_test(&file->device->refcount)) ++ ib_uverbs_comp_dev(file->device); + + kfree(file); + } +@@ -743,9 +753,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp) + int ret; + + dev = container_of(inode->i_cdev, struct ib_uverbs_device, cdev); +- if (dev) +- kref_get(&dev->ref); +- else ++ if (!atomic_inc_not_zero(&dev->refcount)) + return -ENXIO; + + if (!try_module_get(dev->ib_dev->owner)) { +@@ -766,6 +774,7 @@ static int ib_uverbs_open(struct inode *inode, struct file *filp) + mutex_init(&file->mutex); + + filp->private_data = file; ++ kobject_get(&dev->kobj); + + return nonseekable_open(inode, filp); + +@@ -773,13 +782,16 @@ err_module: + module_put(dev->ib_dev->owner); + + err: +- kref_put(&dev->ref, ib_uverbs_release_dev); ++ if (atomic_dec_and_test(&dev->refcount)) ++ ib_uverbs_comp_dev(dev); ++ + return ret; + } + + static int ib_uverbs_close(struct inode *inode, struct file *filp) + { + struct ib_uverbs_file *file = filp->private_data; ++ struct ib_uverbs_device *dev = file->device; + + ib_uverbs_cleanup_ucontext(file, file->ucontext); + +@@ -787,6 +799,7 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp) + kref_put(&file->async_file->ref, ib_uverbs_release_event_file); + + kref_put(&file->ref, ib_uverbs_release_file); ++ kobject_put(&dev->kobj); + + return 0; + } +@@ -882,10 +895,11 @@ static void ib_uverbs_add_one(struct ib_device *device) + if (!uverbs_dev) + return; + +- kref_init(&uverbs_dev->ref); ++ atomic_set(&uverbs_dev->refcount, 1); + init_completion(&uverbs_dev->comp); + uverbs_dev->xrcd_tree = RB_ROOT; + mutex_init(&uverbs_dev->xrcd_tree_mutex); ++ kobject_init(&uverbs_dev->kobj, &ib_uverbs_dev_ktype); + + spin_lock(&map_lock); + devnum = find_first_zero_bit(dev_map, IB_UVERBS_MAX_DEVICES); +@@ -912,6 +926,7 @@ static void ib_uverbs_add_one(struct ib_device *device) + cdev_init(&uverbs_dev->cdev, NULL); + uverbs_dev->cdev.owner = THIS_MODULE; + uverbs_dev->cdev.ops = device->mmap ? &uverbs_mmap_fops : &uverbs_fops; ++ uverbs_dev->cdev.kobj.parent = &uverbs_dev->kobj; + kobject_set_name(&uverbs_dev->cdev.kobj, "uverbs%d", uverbs_dev->devnum); + if (cdev_add(&uverbs_dev->cdev, base, 1)) + goto err_cdev; +@@ -942,9 +957,10 @@ err_cdev: + clear_bit(devnum, overflow_map); + + err: +- kref_put(&uverbs_dev->ref, ib_uverbs_release_dev); ++ if (atomic_dec_and_test(&uverbs_dev->refcount)) ++ ib_uverbs_comp_dev(uverbs_dev); + wait_for_completion(&uverbs_dev->comp); +- kfree(uverbs_dev); ++ kobject_put(&uverbs_dev->kobj); + return; + } + +@@ -964,9 +980,10 @@ static void ib_uverbs_remove_one(struct ib_device *device) + else + clear_bit(uverbs_dev->devnum - IB_UVERBS_MAX_DEVICES, overflow_map); + +- kref_put(&uverbs_dev->ref, ib_uverbs_release_dev); ++ if (atomic_dec_and_test(&uverbs_dev->refcount)) ++ ib_uverbs_comp_dev(uverbs_dev); + wait_for_completion(&uverbs_dev->comp); +- kfree(uverbs_dev); ++ kobject_put(&uverbs_dev->kobj); + } + + static char *uverbs_devnode(struct device *dev, umode_t *mode) +diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c +index f50a546224ad..33fdd50123f7 100644 +--- a/drivers/infiniband/hw/mlx4/ah.c ++++ b/drivers/infiniband/hw/mlx4/ah.c +@@ -148,9 +148,13 @@ int mlx4_ib_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr) + enum rdma_link_layer ll; + + memset(ah_attr, 0, sizeof *ah_attr); +- ah_attr->sl = be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28; + ah_attr->port_num = be32_to_cpu(ah->av.ib.port_pd) >> 24; + ll = rdma_port_get_link_layer(ibah->device, ah_attr->port_num); ++ if (ll == IB_LINK_LAYER_ETHERNET) ++ ah_attr->sl = be32_to_cpu(ah->av.eth.sl_tclass_flowlabel) >> 29; ++ else ++ ah_attr->sl = be32_to_cpu(ah->av.ib.sl_tclass_flowlabel) >> 28; ++ + ah_attr->dlid = ll == IB_LINK_LAYER_INFINIBAND ? be16_to_cpu(ah->av.ib.dlid) : 0; + if (ah->av.ib.stat_rate) + ah_attr->static_rate = ah->av.ib.stat_rate - MLX4_STAT_RATE_OFFSET; +diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c +index 36eb3d012b6d..2f4259525bb1 100644 +--- a/drivers/infiniband/hw/mlx4/cq.c ++++ b/drivers/infiniband/hw/mlx4/cq.c +@@ -638,7 +638,7 @@ static void mlx4_ib_poll_sw_comp(struct mlx4_ib_cq *cq, int num_entries, + * simulated FLUSH_ERR completions + */ + list_for_each_entry(qp, &cq->send_qp_list, cq_send_list) { +- mlx4_ib_qp_sw_comp(qp, num_entries, wc, npolled, 1); ++ mlx4_ib_qp_sw_comp(qp, num_entries, wc + *npolled, npolled, 1); + if (*npolled >= num_entries) + goto out; + } +diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c +index ed327e6c8fdc..a0559a8af4f4 100644 +--- a/drivers/infiniband/hw/mlx4/mcg.c ++++ b/drivers/infiniband/hw/mlx4/mcg.c +@@ -206,15 +206,16 @@ static int send_mad_to_wire(struct mlx4_ib_demux_ctx *ctx, struct ib_mad *mad) + { + struct mlx4_ib_dev *dev = ctx->dev; + struct ib_ah_attr ah_attr; ++ unsigned long flags; + +- spin_lock(&dev->sm_lock); ++ spin_lock_irqsave(&dev->sm_lock, flags); + if (!dev->sm_ah[ctx->port - 1]) { + /* port is not yet Active, sm_ah not ready */ +- spin_unlock(&dev->sm_lock); ++ spin_unlock_irqrestore(&dev->sm_lock, flags); + return -EAGAIN; + } + mlx4_ib_query_ah(dev->sm_ah[ctx->port - 1], &ah_attr); +- spin_unlock(&dev->sm_lock); ++ spin_unlock_irqrestore(&dev->sm_lock, flags); + return mlx4_ib_send_to_wire(dev, mlx4_master_func_num(dev->dev), + ctx->port, IB_QPT_GSI, 0, 1, IB_QP1_QKEY, + &ah_attr, NULL, mad); +diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c +index 6797108ce873..69fb5ba94d0f 100644 +--- a/drivers/infiniband/hw/mlx4/sysfs.c ++++ b/drivers/infiniband/hw/mlx4/sysfs.c +@@ -640,6 +640,8 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) + struct mlx4_port *p; + int i; + int ret; ++ int is_eth = rdma_port_get_link_layer(&dev->ib_dev, port_num) == ++ IB_LINK_LAYER_ETHERNET; + + p = kzalloc(sizeof *p, GFP_KERNEL); + if (!p) +@@ -657,7 +659,8 @@ static int add_port(struct mlx4_ib_dev *dev, int port_num, int slave) + + p->pkey_group.name = "pkey_idx"; + p->pkey_group.attrs = +- alloc_group_attrs(show_port_pkey, store_port_pkey, ++ alloc_group_attrs(show_port_pkey, ++ is_eth ? NULL : store_port_pkey, + dev->dev->caps.pkey_table_len[port_num]); + if (!p->pkey_group.attrs) { + ret = -ENOMEM; +diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c +index bc9a0de897cb..dbb75c0de848 100644 +--- a/drivers/infiniband/hw/mlx5/mr.c ++++ b/drivers/infiniband/hw/mlx5/mr.c +@@ -1118,19 +1118,7 @@ struct ib_mr *mlx5_ib_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + return &mr->ibmr; + + error: +- /* +- * Destroy the umem *before* destroying the MR, to ensure we +- * will not have any in-flight notifiers when destroying the +- * MR. +- * +- * As the MR is completely invalid to begin with, and this +- * error path is only taken if we can't push the mr entry into +- * the pagefault tree, this is safe. +- */ +- + ib_umem_release(umem); +- /* Kill the MR, and return an error code. */ +- clean_mr(mr); + return ERR_PTR(err); + } + +diff --git a/drivers/infiniband/hw/qib/qib_keys.c b/drivers/infiniband/hw/qib/qib_keys.c +index ad843c786e72..5afaa218508d 100644 +--- a/drivers/infiniband/hw/qib/qib_keys.c ++++ b/drivers/infiniband/hw/qib/qib_keys.c +@@ -86,6 +86,10 @@ int qib_alloc_lkey(struct qib_mregion *mr, int dma_region) + * unrestricted LKEY. + */ + rkt->gen++; ++ /* ++ * bits are capped in qib_verbs.c to insure enough bits ++ * for generation number ++ */ + mr->lkey = (r << (32 - ib_qib_lkey_table_size)) | + ((((1 << (24 - ib_qib_lkey_table_size)) - 1) & rkt->gen) + << 8); +diff --git a/drivers/infiniband/hw/qib/qib_verbs.c b/drivers/infiniband/hw/qib/qib_verbs.c +index a05d1a372208..77e981abfce4 100644 +--- a/drivers/infiniband/hw/qib/qib_verbs.c ++++ b/drivers/infiniband/hw/qib/qib_verbs.c +@@ -40,6 +40,7 @@ + #include + #include + #include ++#include + + #include "qib.h" + #include "qib_common.h" +@@ -2109,10 +2110,16 @@ int qib_register_ib_device(struct qib_devdata *dd) + * the LKEY). The remaining bits act as a generation number or tag. + */ + spin_lock_init(&dev->lk_table.lock); ++ /* insure generation is at least 4 bits see keys.c */ ++ if (ib_qib_lkey_table_size > MAX_LKEY_TABLE_BITS) { ++ qib_dev_warn(dd, "lkey bits %u too large, reduced to %u\n", ++ ib_qib_lkey_table_size, MAX_LKEY_TABLE_BITS); ++ ib_qib_lkey_table_size = MAX_LKEY_TABLE_BITS; ++ } + dev->lk_table.max = 1 << ib_qib_lkey_table_size; + lk_tab_size = dev->lk_table.max * sizeof(*dev->lk_table.table); + dev->lk_table.table = (struct qib_mregion __rcu **) +- __get_free_pages(GFP_KERNEL, get_order(lk_tab_size)); ++ vmalloc(lk_tab_size); + if (dev->lk_table.table == NULL) { + ret = -ENOMEM; + goto err_lk; +@@ -2286,7 +2293,7 @@ err_tx: + sizeof(struct qib_pio_header), + dev->pio_hdrs, dev->pio_hdrs_phys); + err_hdrs: +- free_pages((unsigned long) dev->lk_table.table, get_order(lk_tab_size)); ++ vfree(dev->lk_table.table); + err_lk: + kfree(dev->qp_table); + err_qpt: +@@ -2340,8 +2347,7 @@ void qib_unregister_ib_device(struct qib_devdata *dd) + sizeof(struct qib_pio_header), + dev->pio_hdrs, dev->pio_hdrs_phys); + lk_tab_size = dev->lk_table.max * sizeof(*dev->lk_table.table); +- free_pages((unsigned long) dev->lk_table.table, +- get_order(lk_tab_size)); ++ vfree(dev->lk_table.table); + kfree(dev->qp_table); + } + +diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h +index 1635572752ce..bce0fa596b4d 100644 +--- a/drivers/infiniband/hw/qib/qib_verbs.h ++++ b/drivers/infiniband/hw/qib/qib_verbs.h +@@ -647,6 +647,8 @@ struct qib_qpn_table { + struct qpn_map map[QPNMAP_ENTRIES]; + }; + ++#define MAX_LKEY_TABLE_BITS 23 ++ + struct qib_lkey_table { + spinlock_t lock; /* protect changes in this struct */ + u32 next; /* next unused index (speeds search) */ +diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c +index 6a594aac2290..c933d882c35c 100644 +--- a/drivers/infiniband/ulp/iser/iscsi_iser.c ++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c +@@ -201,6 +201,7 @@ iser_initialize_task_headers(struct iscsi_task *task, + goto out; + } + ++ tx_desc->mapped = true; + tx_desc->dma_addr = dma_addr; + tx_desc->tx_sg[0].addr = tx_desc->dma_addr; + tx_desc->tx_sg[0].length = ISER_HEADERS_LEN; +@@ -360,16 +361,19 @@ iscsi_iser_task_xmit(struct iscsi_task *task) + static void iscsi_iser_cleanup_task(struct iscsi_task *task) + { + struct iscsi_iser_task *iser_task = task->dd_data; +- struct iser_tx_desc *tx_desc = &iser_task->desc; +- struct iser_conn *iser_conn = task->conn->dd_data; ++ struct iser_tx_desc *tx_desc = &iser_task->desc; ++ struct iser_conn *iser_conn = task->conn->dd_data; + struct iser_device *device = iser_conn->ib_conn.device; + + /* DEVICE_REMOVAL event might have already released the device */ + if (!device) + return; + +- ib_dma_unmap_single(device->ib_device, +- tx_desc->dma_addr, ISER_HEADERS_LEN, DMA_TO_DEVICE); ++ if (likely(tx_desc->mapped)) { ++ ib_dma_unmap_single(device->ib_device, tx_desc->dma_addr, ++ ISER_HEADERS_LEN, DMA_TO_DEVICE); ++ tx_desc->mapped = false; ++ } + + /* mgmt tasks do not need special cleanup */ + if (!task->sc) +diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h +index 262ba1f8ee50..d2b6caf7694d 100644 +--- a/drivers/infiniband/ulp/iser/iscsi_iser.h ++++ b/drivers/infiniband/ulp/iser/iscsi_iser.h +@@ -270,6 +270,7 @@ enum iser_desc_type { + * sg[1] optionally points to either of immediate data + * unsolicited data-out or control + * @num_sge: number sges used on this TX task ++ * @mapped: Is the task header mapped + */ + struct iser_tx_desc { + struct iser_hdr iser_header; +@@ -278,6 +279,7 @@ struct iser_tx_desc { + u64 dma_addr; + struct ib_sge tx_sg[2]; + int num_sge; ++ bool mapped; + }; + + #define ISER_RX_PAD_SIZE (256 - (ISER_RX_PAYLOAD_SIZE + \ +diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c +index 3e2118e8ed87..0a47f42fec24 100644 +--- a/drivers/infiniband/ulp/iser/iser_initiator.c ++++ b/drivers/infiniband/ulp/iser/iser_initiator.c +@@ -454,7 +454,7 @@ int iser_send_data_out(struct iscsi_conn *conn, + unsigned long buf_offset; + unsigned long data_seg_len; + uint32_t itt; +- int err = 0; ++ int err; + struct ib_sge *tx_dsg; + + itt = (__force uint32_t)hdr->itt; +@@ -475,7 +475,9 @@ int iser_send_data_out(struct iscsi_conn *conn, + memcpy(&tx_desc->iscsi_header, hdr, sizeof(struct iscsi_hdr)); + + /* build the tx desc */ +- iser_initialize_task_headers(task, tx_desc); ++ err = iser_initialize_task_headers(task, tx_desc); ++ if (err) ++ goto send_data_out_error; + + mem_reg = &iser_task->rdma_reg[ISER_DIR_OUT]; + tx_dsg = &tx_desc->tx_sg[1]; +@@ -502,7 +504,7 @@ int iser_send_data_out(struct iscsi_conn *conn, + + send_data_out_error: + kmem_cache_free(ig.desc_cache, tx_desc); +- iser_err("conn %p failed err %d\n",conn, err); ++ iser_err("conn %p failed err %d\n", conn, err); + return err; + } + +diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c +index 31a20b462266..ffda44ff9375 100644 +--- a/drivers/infiniband/ulp/srp/ib_srp.c ++++ b/drivers/infiniband/ulp/srp/ib_srp.c +@@ -2757,6 +2757,13 @@ static int srp_sdev_count(struct Scsi_Host *host) + return c; + } + ++/* ++ * Return values: ++ * < 0 upon failure. Caller is responsible for SRP target port cleanup. ++ * 0 and target->state == SRP_TARGET_REMOVED if asynchronous target port ++ * removal has been scheduled. ++ * 0 and target->state != SRP_TARGET_REMOVED upon success. ++ */ + static int srp_add_target(struct srp_host *host, struct srp_target_port *target) + { + struct srp_rport_identifiers ids; +@@ -3262,7 +3269,7 @@ static ssize_t srp_create_target(struct device *dev, + srp_free_ch_ib(target, ch); + srp_free_req_data(target, ch); + target->ch_count = ch - target->ch; +- break; ++ goto connected; + } + } + +@@ -3272,6 +3279,7 @@ static ssize_t srp_create_target(struct device *dev, + node_idx++; + } + ++connected: + target->scsi_host->nr_hw_queues = target->ch_count; + + ret = srp_add_target(host, target); +@@ -3294,6 +3302,8 @@ out: + mutex_unlock(&host->add_target_mutex); + + scsi_host_put(target->scsi_host); ++ if (ret < 0) ++ scsi_host_put(target->scsi_host); + + return ret; + +diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c +index 9d35499faca4..08d496411f75 100644 +--- a/drivers/input/evdev.c ++++ b/drivers/input/evdev.c +@@ -290,19 +290,14 @@ static int evdev_flush(struct file *file, fl_owner_t id) + { + struct evdev_client *client = file->private_data; + struct evdev *evdev = client->evdev; +- int retval; + +- retval = mutex_lock_interruptible(&evdev->mutex); +- if (retval) +- return retval; ++ mutex_lock(&evdev->mutex); + +- if (!evdev->exist || client->revoked) +- retval = -ENODEV; +- else +- retval = input_flush_device(&evdev->handle, file); ++ if (evdev->exist && !client->revoked) ++ input_flush_device(&evdev->handle, file); + + mutex_unlock(&evdev->mutex); +- return retval; ++ return 0; + } + + static void evdev_free(struct device *dev) +diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c +index abeedc9a78c2..2570f2a25dc4 100644 +--- a/drivers/iommu/fsl_pamu.c ++++ b/drivers/iommu/fsl_pamu.c +@@ -41,7 +41,6 @@ struct pamu_isr_data { + + static struct paace *ppaact; + static struct paace *spaact; +-static struct ome *omt __initdata; + + /* + * Table for matching compatible strings, for device tree +@@ -50,7 +49,7 @@ static struct ome *omt __initdata; + * SOCs. For the older SOCs "fsl,qoriq-device-config-1.0" + * string would be used. + */ +-static const struct of_device_id guts_device_ids[] __initconst = { ++static const struct of_device_id guts_device_ids[] = { + { .compatible = "fsl,qoriq-device-config-1.0", }, + { .compatible = "fsl,qoriq-device-config-2.0", }, + {} +@@ -599,7 +598,7 @@ found_cpu_node: + * Memory accesses to QMAN and BMAN private memory need not be coherent, so + * clear the PAACE entry coherency attribute for them. + */ +-static void __init setup_qbman_paace(struct paace *ppaace, int paace_type) ++static void setup_qbman_paace(struct paace *ppaace, int paace_type) + { + switch (paace_type) { + case QMAN_PAACE: +@@ -629,7 +628,7 @@ static void __init setup_qbman_paace(struct paace *ppaace, int paace_type) + * this table to translate device transaction to appropriate corenet + * transaction. + */ +-static void __init setup_omt(struct ome *omt) ++static void setup_omt(struct ome *omt) + { + struct ome *ome; + +@@ -666,7 +665,7 @@ static void __init setup_omt(struct ome *omt) + * Get the maximum number of PAACT table entries + * and subwindows supported by PAMU + */ +-static void __init get_pamu_cap_values(unsigned long pamu_reg_base) ++static void get_pamu_cap_values(unsigned long pamu_reg_base) + { + u32 pc_val; + +@@ -676,9 +675,9 @@ static void __init get_pamu_cap_values(unsigned long pamu_reg_base) + } + + /* Setup PAMU registers pointing to PAACT, SPAACT and OMT */ +-static int __init setup_one_pamu(unsigned long pamu_reg_base, unsigned long pamu_reg_size, +- phys_addr_t ppaact_phys, phys_addr_t spaact_phys, +- phys_addr_t omt_phys) ++static int setup_one_pamu(unsigned long pamu_reg_base, unsigned long pamu_reg_size, ++ phys_addr_t ppaact_phys, phys_addr_t spaact_phys, ++ phys_addr_t omt_phys) + { + u32 *pc; + struct pamu_mmap_regs *pamu_regs; +@@ -720,7 +719,7 @@ static int __init setup_one_pamu(unsigned long pamu_reg_base, unsigned long pamu + } + + /* Enable all device LIODNS */ +-static void __init setup_liodns(void) ++static void setup_liodns(void) + { + int i, len; + struct paace *ppaace; +@@ -846,7 +845,7 @@ struct ccsr_law { + /* + * Create a coherence subdomain for a given memory block. + */ +-static int __init create_csd(phys_addr_t phys, size_t size, u32 csd_port_id) ++static int create_csd(phys_addr_t phys, size_t size, u32 csd_port_id) + { + struct device_node *np; + const __be32 *iprop; +@@ -988,7 +987,7 @@ error: + static const struct { + u32 svr; + u32 port_id; +-} port_id_map[] __initconst = { ++} port_id_map[] = { + {(SVR_P2040 << 8) | 0x10, 0xFF000000}, /* P2040 1.0 */ + {(SVR_P2040 << 8) | 0x11, 0xFF000000}, /* P2040 1.1 */ + {(SVR_P2041 << 8) | 0x10, 0xFF000000}, /* P2041 1.0 */ +@@ -1006,7 +1005,7 @@ static const struct { + + #define SVR_SECURITY 0x80000 /* The Security (E) bit */ + +-static int __init fsl_pamu_probe(struct platform_device *pdev) ++static int fsl_pamu_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + void __iomem *pamu_regs = NULL; +@@ -1022,6 +1021,7 @@ static int __init fsl_pamu_probe(struct platform_device *pdev) + int irq; + phys_addr_t ppaact_phys; + phys_addr_t spaact_phys; ++ struct ome *omt; + phys_addr_t omt_phys; + size_t mem_size = 0; + unsigned int order = 0; +@@ -1200,7 +1200,7 @@ error: + return ret; + } + +-static struct platform_driver fsl_of_pamu_driver __initdata = { ++static struct platform_driver fsl_of_pamu_driver = { + .driver = { + .name = "fsl-of-pamu", + }, +diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c +index 0649b94f5958..7553cb90627f 100644 +--- a/drivers/iommu/intel-iommu.c ++++ b/drivers/iommu/intel-iommu.c +@@ -755,6 +755,7 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu + struct context_entry *context; + u64 *entry; + ++ entry = &root->lo; + if (ecs_enabled(iommu)) { + if (devfn >= 0x80) { + devfn -= 0x80; +@@ -762,7 +763,6 @@ static inline struct context_entry *iommu_context_addr(struct intel_iommu *iommu + } + devfn *= 2; + } +- entry = &root->lo; + if (*entry & 1) + context = phys_to_virt(*entry & VTD_PAGE_MASK); + else { +diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c +index 4e460216bd16..e29d5d7fe220 100644 +--- a/drivers/iommu/io-pgtable-arm.c ++++ b/drivers/iommu/io-pgtable-arm.c +@@ -200,6 +200,10 @@ typedef u64 arm_lpae_iopte; + + static bool selftest_running = false; + ++static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data, ++ unsigned long iova, size_t size, int lvl, ++ arm_lpae_iopte *ptep); ++ + static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, + unsigned long iova, phys_addr_t paddr, + arm_lpae_iopte prot, int lvl, +@@ -207,10 +211,21 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data, + { + arm_lpae_iopte pte = prot; + +- /* We require an unmap first */ + if (iopte_leaf(*ptep, lvl)) { ++ /* We require an unmap first */ + WARN_ON(!selftest_running); + return -EEXIST; ++ } else if (iopte_type(*ptep, lvl) == ARM_LPAE_PTE_TYPE_TABLE) { ++ /* ++ * We need to unmap and free the old table before ++ * overwriting it with a block entry. ++ */ ++ arm_lpae_iopte *tblp; ++ size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data); ++ ++ tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data); ++ if (WARN_ON(__arm_lpae_unmap(data, iova, sz, lvl, tblp) != sz)) ++ return -EINVAL; + } + + if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS) +diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c +index c1f2e521dc52..2cd439203d0f 100644 +--- a/drivers/iommu/tegra-smmu.c ++++ b/drivers/iommu/tegra-smmu.c +@@ -27,6 +27,7 @@ struct tegra_smmu { + const struct tegra_smmu_soc *soc; + + unsigned long pfn_mask; ++ unsigned long tlb_mask; + + unsigned long *asids; + struct mutex lock; +@@ -68,7 +69,8 @@ static inline u32 smmu_readl(struct tegra_smmu *smmu, unsigned long offset) + #define SMMU_TLB_CONFIG 0x14 + #define SMMU_TLB_CONFIG_HIT_UNDER_MISS (1 << 29) + #define SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION (1 << 28) +-#define SMMU_TLB_CONFIG_ACTIVE_LINES(x) ((x) & 0x3f) ++#define SMMU_TLB_CONFIG_ACTIVE_LINES(smmu) \ ++ ((smmu)->soc->num_tlb_lines & (smmu)->tlb_mask) + + #define SMMU_PTC_CONFIG 0x18 + #define SMMU_PTC_CONFIG_ENABLE (1 << 29) +@@ -816,6 +818,9 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, + smmu->pfn_mask = BIT_MASK(mc->soc->num_address_bits - PAGE_SHIFT) - 1; + dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n", + mc->soc->num_address_bits, smmu->pfn_mask); ++ smmu->tlb_mask = (smmu->soc->num_tlb_lines << 1) - 1; ++ dev_dbg(dev, "TLB lines: %u, mask: %#lx\n", smmu->soc->num_tlb_lines, ++ smmu->tlb_mask); + + value = SMMU_PTC_CONFIG_ENABLE | SMMU_PTC_CONFIG_INDEX_MAP(0x3f); + +@@ -825,7 +830,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, + smmu_writel(smmu, value, SMMU_PTC_CONFIG); + + value = SMMU_TLB_CONFIG_HIT_UNDER_MISS | +- SMMU_TLB_CONFIG_ACTIVE_LINES(0x20); ++ SMMU_TLB_CONFIG_ACTIVE_LINES(smmu); + + if (soc->supports_round_robin_arbitration) + value |= SMMU_TLB_CONFIG_ROUND_ROBIN_ARBITRATION; +diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c +index 1fba339cddc1..c8447fa3fd91 100644 +--- a/drivers/media/platform/am437x/am437x-vpfe.c ++++ b/drivers/media/platform/am437x/am437x-vpfe.c +@@ -1186,14 +1186,24 @@ static int vpfe_initialize_device(struct vpfe_device *vpfe) + static int vpfe_release(struct file *file) + { + struct vpfe_device *vpfe = video_drvdata(file); ++ bool fh_singular; + int ret; + + mutex_lock(&vpfe->lock); + +- if (v4l2_fh_is_singular_file(file)) +- vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); ++ /* Save the singular status before we call the clean-up helper */ ++ fh_singular = v4l2_fh_is_singular_file(file); ++ ++ /* the release helper will cleanup any on-going streaming */ + ret = _vb2_fop_release(file, NULL); + ++ /* ++ * If this was the last open file. ++ * Then de-initialize hw module. ++ */ ++ if (fh_singular) ++ vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); ++ + mutex_unlock(&vpfe->lock); + + return ret; +@@ -1565,7 +1575,7 @@ static int vpfe_s_fmt(struct file *file, void *priv, + return -EBUSY; + } + +- ret = vpfe_try_fmt(file, priv, fmt); ++ ret = vpfe_try_fmt(file, priv, &format); + if (ret) + return ret; + +diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c +index 18d0a871747f..12be830d704f 100644 +--- a/drivers/media/platform/omap3isp/isp.c ++++ b/drivers/media/platform/omap3isp/isp.c +@@ -829,14 +829,14 @@ static int isp_pipeline_link_notify(struct media_link *link, u32 flags, + int ret; + + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && +- !(link->flags & MEDIA_LNK_FL_ENABLED)) { ++ !(flags & MEDIA_LNK_FL_ENABLED)) { + /* Powering off entities is assumed to never fail. */ + isp_pipeline_pm_power(source, -sink_use); + isp_pipeline_pm_power(sink, -source_use); + return 0; + } + +- if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && ++ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { + + ret = isp_pipeline_pm_power(source, sink_use); +@@ -2000,10 +2000,8 @@ static int isp_register_entities(struct isp_device *isp) + ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + + done: +- if (ret < 0) { ++ if (ret < 0) + isp_unregister_entities(isp); +- v4l2_async_notifier_unregister(&isp->notifier); +- } + + return ret; + } +@@ -2423,10 +2421,6 @@ static int isp_probe(struct platform_device *pdev) + ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier); + if (ret < 0) + return ret; +- ret = v4l2_async_notifier_register(&isp->v4l2_dev, +- &isp->notifier); +- if (ret) +- return ret; + } else { + isp->pdata = pdev->dev.platform_data; + isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0"); +@@ -2557,18 +2551,27 @@ static int isp_probe(struct platform_device *pdev) + if (ret < 0) + goto error_iommu; + +- isp->notifier.bound = isp_subdev_notifier_bound; +- isp->notifier.complete = isp_subdev_notifier_complete; +- + ret = isp_register_entities(isp); + if (ret < 0) + goto error_modules; + ++ if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { ++ isp->notifier.bound = isp_subdev_notifier_bound; ++ isp->notifier.complete = isp_subdev_notifier_complete; ++ ++ ret = v4l2_async_notifier_register(&isp->v4l2_dev, ++ &isp->notifier); ++ if (ret) ++ goto error_register_entities; ++ } ++ + isp_core_init(isp, 1); + omap3isp_put(isp); + + return 0; + ++error_register_entities: ++ isp_unregister_entities(isp); + error_modules: + isp_cleanup_modules(isp); + error_iommu: +diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c +index 98e50e446d57..e779c93cb015 100644 +--- a/drivers/media/platform/xilinx/xilinx-dma.c ++++ b/drivers/media/platform/xilinx/xilinx-dma.c +@@ -699,8 +699,10 @@ int xvip_dma_init(struct xvip_composite_device *xdev, struct xvip_dma *dma, + + /* ... and the buffers queue... */ + dma->alloc_ctx = vb2_dma_contig_init_ctx(dma->xdev->dev); +- if (IS_ERR(dma->alloc_ctx)) ++ if (IS_ERR(dma->alloc_ctx)) { ++ ret = PTR_ERR(dma->alloc_ctx); + goto error; ++ } + + /* Don't enable VB2_READ and VB2_WRITE, as using the read() and write() + * V4L2 APIs would be inefficient. Testing on the command line with a +diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c +index 0ff388a16168..f3b6b2caabf6 100644 +--- a/drivers/media/rc/rc-main.c ++++ b/drivers/media/rc/rc-main.c +@@ -1191,9 +1191,6 @@ static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) + { + struct rc_dev *dev = to_rc_dev(device); + +- if (!dev || !dev->input_dev) +- return -ENODEV; +- + if (dev->rc_map.name) + ADD_HOTPLUG_VAR("NAME=%s", dev->rc_map.name); + if (dev->driver_name) +diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c +index 9f579589e800..9bf11ea90549 100644 +--- a/drivers/memory/tegra/tegra114.c ++++ b/drivers/memory/tegra/tegra114.c +@@ -935,6 +935,7 @@ static const struct tegra_smmu_soc tegra114_smmu_soc = { + .num_swgroups = ARRAY_SIZE(tegra114_swgroups), + .supports_round_robin_arbitration = false, + .supports_request_limit = false, ++ .num_tlb_lines = 32, + .num_asids = 4, + .ops = &tegra114_smmu_ops, + }; +diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c +index 966e1557e6f4..70ed80d23431 100644 +--- a/drivers/memory/tegra/tegra124.c ++++ b/drivers/memory/tegra/tegra124.c +@@ -1023,6 +1023,7 @@ static const struct tegra_smmu_soc tegra124_smmu_soc = { + .num_swgroups = ARRAY_SIZE(tegra124_swgroups), + .supports_round_robin_arbitration = true, + .supports_request_limit = true, ++ .num_tlb_lines = 32, + .num_asids = 128, + .ops = &tegra124_smmu_ops, + }; +diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c +index 1abcd8f6f3ba..b2a34fefabef 100644 +--- a/drivers/memory/tegra/tegra30.c ++++ b/drivers/memory/tegra/tegra30.c +@@ -957,6 +957,7 @@ static const struct tegra_smmu_soc tegra30_smmu_soc = { + .num_swgroups = ARRAY_SIZE(tegra30_swgroups), + .supports_round_robin_arbitration = false, + .supports_request_limit = false, ++ .num_tlb_lines = 16, + .num_asids = 4, + .ops = &tegra30_smmu_ops, + }; +diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c +index 729e0851167d..4224a6acf4c4 100644 +--- a/drivers/misc/cxl/api.c ++++ b/drivers/misc/cxl/api.c +@@ -59,7 +59,7 @@ EXPORT_SYMBOL_GPL(cxl_get_phys_dev); + + int cxl_release_context(struct cxl_context *ctx) + { +- if (ctx->status != CLOSED) ++ if (ctx->status >= STARTED) + return -EBUSY; + + put_device(&ctx->afu->dev); +diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c +index 32ad09705949..dc836071c633 100644 +--- a/drivers/misc/cxl/pci.c ++++ b/drivers/misc/cxl/pci.c +@@ -851,16 +851,9 @@ int cxl_reset(struct cxl *adapter) + { + struct pci_dev *dev = to_pci_dev(adapter->dev.parent); + int rc; +- int i; +- u32 val; + + dev_info(&dev->dev, "CXL reset\n"); + +- for (i = 0; i < adapter->slices; i++) { +- cxl_pci_vphb_remove(adapter->afu[i]); +- cxl_remove_afu(adapter->afu[i]); +- } +- + /* pcie_warm_reset requests a fundamental pci reset which includes a + * PERST assert/deassert. PERST triggers a loading of the image + * if "user" or "factory" is selected in sysfs */ +@@ -869,20 +862,6 @@ int cxl_reset(struct cxl *adapter) + return rc; + } + +- /* the PERST done above fences the PHB. So, reset depends on EEH +- * to unbind the driver, tell Sapphire to reinit the PHB, and rebind +- * the driver. Do an mmio read explictly to ensure EEH notices the +- * fenced PHB. Retry for a few seconds before giving up. */ +- i = 0; +- while (((val = mmio_read32be(adapter->p1_mmio)) != 0xffffffff) && +- (i < 5)) { +- msleep(500); +- i++; +- } +- +- if (val != 0xffffffff) +- dev_err(&dev->dev, "cxl: PERST failed to trigger EEH\n"); +- + return rc; + } + +@@ -1140,8 +1119,6 @@ static int cxl_probe(struct pci_dev *dev, const struct pci_device_id *id) + int slice; + int rc; + +- pci_dev_get(dev); +- + if (cxl_verbose) + dump_cxl_config_space(dev); + +diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c +index 797be7549a15..653f335bef15 100644 +--- a/drivers/mmc/host/sdhci-of-esdhc.c ++++ b/drivers/mmc/host/sdhci-of-esdhc.c +@@ -208,6 +208,12 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) + if (clock == 0) + return; + ++ /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ ++ temp = esdhc_readw(host, SDHCI_HOST_VERSION); ++ temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; ++ if (temp < VENDOR_V_23) ++ pre_div = 2; ++ + /* Workaround to reduce the clock frequency for p1010 esdhc */ + if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { + if (clock > 20000000) +diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c +index 94f54d2772e8..b3b0a3e4fca1 100644 +--- a/drivers/mmc/host/sdhci-pci.c ++++ b/drivers/mmc/host/sdhci-pci.c +@@ -618,6 +618,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) + static const struct sdhci_pci_fixes sdhci_o2 = { + .probe = sdhci_pci_o2_probe, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, ++ .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, + .probe_slot = sdhci_pci_o2_probe_slot, + .resume = sdhci_pci_o2_resume, + }; +diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c +index 73c934cf6c61..79789d8e52da 100644 +--- a/drivers/net/ethernet/broadcom/tg3.c ++++ b/drivers/net/ethernet/broadcom/tg3.c +@@ -10757,7 +10757,7 @@ static ssize_t tg3_show_temp(struct device *dev, + tg3_ape_scratchpad_read(tp, &temperature, attr->index, + sizeof(temperature)); + spin_unlock_bh(&tp->lock); +- return sprintf(buf, "%u\n", temperature); ++ return sprintf(buf, "%u\n", temperature * 1000); + } + + +diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h +index c2bd4f98a837..212d668dabb3 100644 +--- a/drivers/net/ethernet/intel/igb/igb.h ++++ b/drivers/net/ethernet/intel/igb/igb.h +@@ -540,6 +540,7 @@ void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va, + struct sk_buff *skb); + int igb_ptp_set_ts_config(struct net_device *netdev, struct ifreq *ifr); + int igb_ptp_get_ts_config(struct net_device *netdev, struct ifreq *ifr); ++void igb_set_flag_queue_pairs(struct igb_adapter *, const u32); + #ifdef CONFIG_IGB_HWMON + void igb_sysfs_exit(struct igb_adapter *adapter); + int igb_sysfs_init(struct igb_adapter *adapter); +diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c +index d5673eb90c54..0afc0913e5b9 100644 +--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c ++++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c +@@ -2991,6 +2991,7 @@ static int igb_set_channels(struct net_device *netdev, + { + struct igb_adapter *adapter = netdev_priv(netdev); + unsigned int count = ch->combined_count; ++ unsigned int max_combined = 0; + + /* Verify they are not requesting separate vectors */ + if (!count || ch->rx_count || ch->tx_count) +@@ -3001,11 +3002,13 @@ static int igb_set_channels(struct net_device *netdev, + return -EINVAL; + + /* Verify the number of channels doesn't exceed hw limits */ +- if (count > igb_max_channels(adapter)) ++ max_combined = igb_max_channels(adapter); ++ if (count > max_combined) + return -EINVAL; + + if (count != adapter->rss_queues) { + adapter->rss_queues = count; ++ igb_set_flag_queue_pairs(adapter, max_combined); + + /* Hardware has to reinitialize queues and interrupts to + * match the new configuration. +diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c +index 830466c49987..8d7b59689722 100644 +--- a/drivers/net/ethernet/intel/igb/igb_main.c ++++ b/drivers/net/ethernet/intel/igb/igb_main.c +@@ -1205,10 +1205,14 @@ static int igb_alloc_q_vector(struct igb_adapter *adapter, + + /* allocate q_vector and rings */ + q_vector = adapter->q_vector[v_idx]; +- if (!q_vector) ++ if (!q_vector) { + q_vector = kzalloc(size, GFP_KERNEL); +- else ++ } else if (size > ksize(q_vector)) { ++ kfree_rcu(q_vector, rcu); ++ q_vector = kzalloc(size, GFP_KERNEL); ++ } else { + memset(q_vector, 0, size); ++ } + if (!q_vector) + return -ENOMEM; + +@@ -2888,6 +2892,14 @@ static void igb_init_queue_configuration(struct igb_adapter *adapter) + + adapter->rss_queues = min_t(u32, max_rss_queues, num_online_cpus()); + ++ igb_set_flag_queue_pairs(adapter, max_rss_queues); ++} ++ ++void igb_set_flag_queue_pairs(struct igb_adapter *adapter, ++ const u32 max_rss_queues) ++{ ++ struct e1000_hw *hw = &adapter->hw; ++ + /* Determine if we need to pair queues. */ + switch (hw->mac.type) { + case e1000_82575: +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 864b476f7fd5..925f2f8659b8 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -837,8 +837,11 @@ static int stmmac_init_phy(struct net_device *dev) + interface); + } + +- if (IS_ERR(phydev)) { ++ if (IS_ERR_OR_NULL(phydev)) { + pr_err("%s: Could not attach to PHY\n", dev->name); ++ if (!phydev) ++ return -ENODEV; ++ + return PTR_ERR(phydev); + } + +diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +index 23806c243a53..fd4a5353d216 100644 +--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c ++++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +@@ -321,6 +321,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { + {RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/ + {RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/ + {RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/ ++ {RTL_USB_DEVICE(0x0846, 0x9043, rtl92cu_hal_cfg)}, /*NG WNA1000Mv2*/ + {RTL_USB_DEVICE(0x0b05, 0x17ba, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/ + {RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/ + {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ +diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +index 3236d44b459d..b7f18e2155eb 100644 +--- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c ++++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +@@ -2180,7 +2180,7 @@ static int _rtl8821ae_set_media_status(struct ieee80211_hw *hw, + + rtl_write_byte(rtlpriv, MSR, bt_msr); + rtlpriv->cfg->ops->led_control(hw, ledaction); +- if ((bt_msr & 0xfc) == MSR_AP) ++ if ((bt_msr & MSR_MASK) == MSR_AP) + rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); + else + rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); +diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/reg.h b/drivers/net/wireless/rtlwifi/rtl8821ae/reg.h +index 53668fc8f23e..1d6110f9c1fb 100644 +--- a/drivers/net/wireless/rtlwifi/rtl8821ae/reg.h ++++ b/drivers/net/wireless/rtlwifi/rtl8821ae/reg.h +@@ -429,6 +429,7 @@ + #define MSR_ADHOC 0x01 + #define MSR_INFRA 0x02 + #define MSR_AP 0x03 ++#define MSR_MASK 0x03 + + #define RRSR_RSC_OFFSET 21 + #define RRSR_SHORT_OFFSET 23 +diff --git a/drivers/nfc/st-nci/i2c.c b/drivers/nfc/st-nci/i2c.c +index 06175ce769bb..707ed2eb5936 100644 +--- a/drivers/nfc/st-nci/i2c.c ++++ b/drivers/nfc/st-nci/i2c.c +@@ -25,15 +25,15 @@ + #include + #include + #include +-#include ++#include + + #include "ndlc.h" + +-#define DRIVER_DESC "NCI NFC driver for ST21NFCB" ++#define DRIVER_DESC "NCI NFC driver for ST_NCI" + + /* ndlc header */ +-#define ST21NFCB_FRAME_HEADROOM 1 +-#define ST21NFCB_FRAME_TAILROOM 0 ++#define ST_NCI_FRAME_HEADROOM 1 ++#define ST_NCI_FRAME_TAILROOM 0 + + #define ST_NCI_I2C_MIN_SIZE 4 /* PCB(1) + NCI Packet header(3) */ + #define ST_NCI_I2C_MAX_SIZE 250 /* req 4.2.1 */ +@@ -118,15 +118,10 @@ static int st_nci_i2c_write(void *phy_id, struct sk_buff *skb) + /* + * Reads an ndlc frame and returns it in a newly allocated sk_buff. + * returns: +- * frame size : if received frame is complete (find ST21NFCB_SOF_EOF at +- * end of read) +- * -EAGAIN : if received frame is incomplete (not find ST21NFCB_SOF_EOF +- * at end of read) ++ * 0 : if received frame is complete + * -EREMOTEIO : i2c read error (fatal) + * -EBADMSG : frame was incorrect and discarded +- * (value returned from st_nci_i2c_repack) +- * -EIO : if no ST21NFCB_SOF_EOF is found after reaching +- * the read length end sequence ++ * -ENOMEM : cannot allocate skb, frame dropped + */ + static int st_nci_i2c_read(struct st_nci_i2c_phy *phy, + struct sk_buff **skb) +@@ -179,7 +174,7 @@ static int st_nci_i2c_read(struct st_nci_i2c_phy *phy, + /* + * Reads an ndlc frame from the chip. + * +- * On ST21NFCB, IRQ goes in idle state when read starts. ++ * On ST_NCI, IRQ goes in idle state when read starts. + */ + static irqreturn_t st_nci_irq_thread_fn(int irq, void *phy_id) + { +@@ -325,12 +320,12 @@ static int st_nci_i2c_probe(struct i2c_client *client, + } + } else { + nfc_err(&client->dev, +- "st21nfcb platform resources not available\n"); ++ "st_nci platform resources not available\n"); + return -ENODEV; + } + + r = ndlc_probe(phy, &i2c_phy_ops, &client->dev, +- ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM, ++ ST_NCI_FRAME_HEADROOM, ST_NCI_FRAME_TAILROOM, + &phy->ndlc); + if (r < 0) { + nfc_err(&client->dev, "Unable to register ndlc layer\n"); +diff --git a/drivers/nfc/st-nci/ndlc.c b/drivers/nfc/st-nci/ndlc.c +index 56c6a4cb4c96..4f51649d0e75 100644 +--- a/drivers/nfc/st-nci/ndlc.c ++++ b/drivers/nfc/st-nci/ndlc.c +@@ -171,6 +171,8 @@ static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) + if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { + switch (pcb & PCB_SYNC_MASK) { + case PCB_SYNC_ACK: ++ skb = skb_dequeue(&ndlc->ack_pending_q); ++ kfree_skb(skb); + del_timer_sync(&ndlc->t1_timer); + del_timer_sync(&ndlc->t2_timer); + ndlc->t2_active = false; +@@ -196,8 +198,10 @@ static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) + kfree_skb(skb); + break; + } +- } else { ++ } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) { + nci_recv_frame(ndlc->ndev, skb); ++ } else { ++ kfree_skb(skb); + } + } + } +diff --git a/drivers/nfc/st-nci/st-nci_se.c b/drivers/nfc/st-nci/st-nci_se.c +index 97addfa96c6f..c742ef65a05a 100644 +--- a/drivers/nfc/st-nci/st-nci_se.c ++++ b/drivers/nfc/st-nci/st-nci_se.c +@@ -189,14 +189,14 @@ int st_nci_hci_load_session(struct nci_dev *ndev) + ST_NCI_DEVICE_MGNT_GATE, + ST_NCI_DEVICE_MGNT_PIPE); + if (r < 0) +- goto free_info; ++ return r; + + /* Get pipe list */ + r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE, + ST_NCI_DM_GETINFO, pipe_list, sizeof(pipe_list), + &skb_pipe_list); + if (r < 0) +- goto free_info; ++ return r; + + /* Complete the existing gate_pipe table */ + for (i = 0; i < skb_pipe_list->len; i++) { +@@ -222,6 +222,7 @@ int st_nci_hci_load_session(struct nci_dev *ndev) + dm_pipe_info->src_host_id != ST_NCI_ESE_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + dm_pipe_info->src_host_id); ++ kfree_skb(skb_pipe_info); + continue; + } + +@@ -241,13 +242,12 @@ int st_nci_hci_load_session(struct nci_dev *ndev) + ndev->hci_dev->pipes[st_nci_gates[j].pipe].host = + dm_pipe_info->src_host_id; + } ++ kfree_skb(skb_pipe_info); + } + + memcpy(ndev->hci_dev->init_data.gates, st_nci_gates, + sizeof(st_nci_gates)); + +-free_info: +- kfree_skb(skb_pipe_info); + kfree_skb(skb_pipe_list); + return r; + } +diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c +index d251f7229c4e..051286562fab 100644 +--- a/drivers/nfc/st21nfca/st21nfca.c ++++ b/drivers/nfc/st21nfca/st21nfca.c +@@ -148,14 +148,14 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DEVICE_MGNT_PIPE); + if (r < 0) +- goto free_info; ++ return r; + + /* Get pipe list */ + r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list), + &skb_pipe_list); + if (r < 0) +- goto free_info; ++ return r; + + /* Complete the existing gate_pipe table */ + for (i = 0; i < skb_pipe_list->len; i++) { +@@ -181,6 +181,7 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) + info->src_host_id != ST21NFCA_ESE_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + info->src_host_id); ++ kfree_skb(skb_pipe_info); + continue; + } + +@@ -200,6 +201,7 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) + hdev->pipes[st21nfca_gates[j].pipe].dest_host = + info->src_host_id; + } ++ kfree_skb(skb_pipe_info); + } + + /* +@@ -214,13 +216,12 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) + st21nfca_gates[i].gate, + st21nfca_gates[i].pipe); + if (r < 0) +- goto free_info; ++ goto free_list; + } + } + + memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); +-free_info: +- kfree_skb(skb_pipe_info); ++free_list: + kfree_skb(skb_pipe_list); + return r; + } +diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c +index 07496560e5b9..6e82bc42373b 100644 +--- a/drivers/of/fdt.c ++++ b/drivers/of/fdt.c +@@ -967,7 +967,9 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, + } + + #ifdef CONFIG_HAVE_MEMBLOCK +-#define MAX_PHYS_ADDR ((phys_addr_t)~0) ++#ifndef MAX_MEMBLOCK_ADDR ++#define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0) ++#endif + + void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) + { +@@ -984,16 +986,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) + } + size &= PAGE_MASK; + +- if (base > MAX_PHYS_ADDR) { ++ if (base > MAX_MEMBLOCK_ADDR) { + pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", + base, base + size); + return; + } + +- if (base + size - 1 > MAX_PHYS_ADDR) { ++ if (base + size - 1 > MAX_MEMBLOCK_ADDR) { + pr_warning("Ignoring memory range 0x%llx - 0x%llx\n", +- ((u64)MAX_PHYS_ADDR) + 1, base + size); +- size = MAX_PHYS_ADDR - base + 1; ++ ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); ++ size = MAX_MEMBLOCK_ADDR - base + 1; + } + + if (base + size < phys_offset) { +diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c +index dceb9ddfd99a..a32c1f6c252c 100644 +--- a/drivers/parisc/lba_pci.c ++++ b/drivers/parisc/lba_pci.c +@@ -1556,8 +1556,11 @@ lba_driver_probe(struct parisc_device *dev) + if (lba_dev->hba.lmmio_space.flags) + pci_add_resource_offset(&resources, &lba_dev->hba.lmmio_space, + lba_dev->hba.lmmio_space_offset); +- if (lba_dev->hba.gmmio_space.flags) +- pci_add_resource(&resources, &lba_dev->hba.gmmio_space); ++ if (lba_dev->hba.gmmio_space.flags) { ++ /* pci_add_resource(&resources, &lba_dev->hba.gmmio_space); */ ++ pr_warn("LBA: Not registering GMMIO space %pR\n", ++ &lba_dev->hba.gmmio_space); ++ } + + pci_add_resource(&resources, &lba_dev->hba.bus_num); + +diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig +index 944f50015ed0..73de4efcbe6e 100644 +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -2,7 +2,7 @@ + # PCI configuration + # + config PCI_BUS_ADDR_T_64BIT +- def_bool y if (ARCH_DMA_ADDR_T_64BIT || (64BIT && !PARISC)) ++ def_bool y if (ARCH_DMA_ADDR_T_64BIT || 64BIT) + depends on PCI + + config PCI_MSI +diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +index ad1ea1695b4a..4a52072d1d3f 100644 +--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c ++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +@@ -1202,12 +1202,6 @@ static int mtk_pctrl_build_state(struct platform_device *pdev) + return 0; + } + +-static struct pinctrl_desc mtk_pctrl_desc = { +- .confops = &mtk_pconf_ops, +- .pctlops = &mtk_pctrl_ops, +- .pmxops = &mtk_pmx_ops, +-}; +- + int mtk_pctrl_init(struct platform_device *pdev, + const struct mtk_pinctrl_devdata *data, + struct regmap *regmap) +@@ -1265,12 +1259,17 @@ int mtk_pctrl_init(struct platform_device *pdev, + + for (i = 0; i < pctl->devdata->npins; i++) + pins[i] = pctl->devdata->pins[i].pin; +- mtk_pctrl_desc.name = dev_name(&pdev->dev); +- mtk_pctrl_desc.owner = THIS_MODULE; +- mtk_pctrl_desc.pins = pins; +- mtk_pctrl_desc.npins = pctl->devdata->npins; ++ ++ pctl->pctl_desc.name = dev_name(&pdev->dev); ++ pctl->pctl_desc.owner = THIS_MODULE; ++ pctl->pctl_desc.pins = pins; ++ pctl->pctl_desc.npins = pctl->devdata->npins; ++ pctl->pctl_desc.confops = &mtk_pconf_ops; ++ pctl->pctl_desc.pctlops = &mtk_pctrl_ops; ++ pctl->pctl_desc.pmxops = &mtk_pmx_ops; + pctl->dev = &pdev->dev; +- pctl->pctl_dev = pinctrl_register(&mtk_pctrl_desc, &pdev->dev, pctl); ++ ++ pctl->pctl_dev = pinctrl_register(&pctl->pctl_desc, &pdev->dev, pctl); + if (IS_ERR(pctl->pctl_dev)) { + dev_err(&pdev->dev, "couldn't register pinctrl driver\n"); + return PTR_ERR(pctl->pctl_dev); +diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h +index 30213e514c2f..c532c23c70b4 100644 +--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.h ++++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.h +@@ -256,6 +256,7 @@ struct mtk_pinctrl_devdata { + struct mtk_pinctrl { + struct regmap *regmap1; + struct regmap *regmap2; ++ struct pinctrl_desc pctl_desc; + struct device *dev; + struct gpio_chip *chip; + struct mtk_pinctrl_group *groups; +diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c +index a0824477072b..2deb1309fcac 100644 +--- a/drivers/pinctrl/pinctrl-at91.c ++++ b/drivers/pinctrl/pinctrl-at91.c +@@ -320,6 +320,9 @@ static const struct pinctrl_ops at91_pctrl_ops = { + static void __iomem *pin_to_controller(struct at91_pinctrl *info, + unsigned int bank) + { ++ if (!gpio_chips[bank]) ++ return NULL; ++ + return gpio_chips[bank]->regbase; + } + +@@ -729,6 +732,10 @@ static int at91_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, + pin = &pins_conf[i]; + at91_pin_dbg(info->dev, pin); + pio = pin_to_controller(info, pin->bank); ++ ++ if (!pio) ++ continue; ++ + mask = pin_to_mask(pin->pin); + at91_mux_disable_interrupt(pio, mask); + switch (pin->mux) { +@@ -848,6 +855,10 @@ static int at91_pinconf_get(struct pinctrl_dev *pctldev, + *config = 0; + dev_dbg(info->dev, "%s:%d, pin_id=%d", __func__, __LINE__, pin_id); + pio = pin_to_controller(info, pin_to_bank(pin_id)); ++ ++ if (!pio) ++ return -EINVAL; ++ + pin = pin_id % MAX_NB_GPIO_PER_BANK; + + if (at91_mux_get_multidrive(pio, pin)) +@@ -889,6 +900,10 @@ static int at91_pinconf_set(struct pinctrl_dev *pctldev, + "%s:%d, pin_id=%d, config=0x%lx", + __func__, __LINE__, pin_id, config); + pio = pin_to_controller(info, pin_to_bank(pin_id)); ++ ++ if (!pio) ++ return -EINVAL; ++ + pin = pin_id % MAX_NB_GPIO_PER_BANK; + mask = pin_to_mask(pin); + +diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c +index 76b57388d01b..81c3e582309a 100644 +--- a/drivers/platform/x86/ideapad-laptop.c ++++ b/drivers/platform/x86/ideapad-laptop.c +@@ -853,6 +853,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { + }, + }, + { ++ .ident = "Lenovo Yoga 3 14", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 3 14"), ++ }, ++ }, ++ { + .ident = "Lenovo Yoga 3 Pro 1370", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c +index 4337c3bc6ace..afea84c7a155 100644 +--- a/drivers/rtc/rtc-abx80x.c ++++ b/drivers/rtc/rtc-abx80x.c +@@ -28,7 +28,7 @@ + #define ABX8XX_REG_WD 0x07 + + #define ABX8XX_REG_CTRL1 0x10 +-#define ABX8XX_CTRL_WRITE BIT(1) ++#define ABX8XX_CTRL_WRITE BIT(0) + #define ABX8XX_CTRL_12_24 BIT(6) + + #define ABX8XX_REG_CFG_KEY 0x1f +diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c +index a0f832362199..2e709e239dbc 100644 +--- a/drivers/rtc/rtc-s3c.c ++++ b/drivers/rtc/rtc-s3c.c +@@ -39,6 +39,7 @@ struct s3c_rtc { + void __iomem *base; + struct clk *rtc_clk; + struct clk *rtc_src_clk; ++ bool clk_disabled; + + struct s3c_rtc_data *data; + +@@ -71,9 +72,12 @@ static void s3c_rtc_enable_clk(struct s3c_rtc *info) + unsigned long irq_flags; + + spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); +- clk_enable(info->rtc_clk); +- if (info->data->needs_src_clk) +- clk_enable(info->rtc_src_clk); ++ if (info->clk_disabled) { ++ clk_enable(info->rtc_clk); ++ if (info->data->needs_src_clk) ++ clk_enable(info->rtc_src_clk); ++ info->clk_disabled = false; ++ } + spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); + } + +@@ -82,9 +86,12 @@ static void s3c_rtc_disable_clk(struct s3c_rtc *info) + unsigned long irq_flags; + + spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); +- if (info->data->needs_src_clk) +- clk_disable(info->rtc_src_clk); +- clk_disable(info->rtc_clk); ++ if (!info->clk_disabled) { ++ if (info->data->needs_src_clk) ++ clk_disable(info->rtc_src_clk); ++ clk_disable(info->rtc_clk); ++ info->clk_disabled = true; ++ } + spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); + } + +@@ -128,6 +135,11 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) + + s3c_rtc_disable_clk(info); + ++ if (enabled) ++ s3c_rtc_enable_clk(info); ++ else ++ s3c_rtc_disable_clk(info); ++ + return 0; + } + +diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c +index 8c70d785ba73..ab60287ee72d 100644 +--- a/drivers/rtc/rtc-s5m.c ++++ b/drivers/rtc/rtc-s5m.c +@@ -635,6 +635,16 @@ static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info) + case S2MPS13X: + data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + ret = regmap_write(info->regmap, info->regs->ctrl, data[0]); ++ if (ret < 0) ++ break; ++ ++ /* ++ * Should set WUDR & (RUDR or AUDR) bits to high after writing ++ * RTC_CTRL register like writing Alarm registers. We can't find ++ * the description from datasheet but vendor code does that ++ * really. ++ */ ++ ret = s5m8767_rtc_set_alarm_reg(info); + break; + + default: +diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c +index f5021fcb154e..089e7f8543a5 100644 +--- a/fs/btrfs/transaction.c ++++ b/fs/btrfs/transaction.c +@@ -1893,8 +1893,11 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, + spin_unlock(&root->fs_info->trans_lock); + + wait_for_commit(root, prev_trans); ++ ret = prev_trans->aborted; + + btrfs_put_transaction(prev_trans); ++ if (ret) ++ goto cleanup_transaction; + } else { + spin_unlock(&root->fs_info->trans_lock); + } +diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c +index 49b8b6e41a18..c7b84f3bf6ad 100644 +--- a/fs/cifs/ioctl.c ++++ b/fs/cifs/ioctl.c +@@ -70,6 +70,12 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, + goto out_drop_write; + } + ++ if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) { ++ rc = -EBADF; ++ cifs_dbg(VFS, "src file seems to be from a different filesystem type\n"); ++ goto out_fput; ++ } ++ + if ((!src_file.file->private_data) || (!dst_file->private_data)) { + rc = -EBADF; + cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); +diff --git a/fs/coredump.c b/fs/coredump.c +index c5ecde6f3eed..a8f75640ac86 100644 +--- a/fs/coredump.c ++++ b/fs/coredump.c +@@ -513,10 +513,10 @@ void do_coredump(const siginfo_t *siginfo) + const struct cred *old_cred; + struct cred *cred; + int retval = 0; +- int flag = 0; + int ispipe; + struct files_struct *displaced; +- bool need_nonrelative = false; ++ /* require nonrelative corefile path and be extra careful */ ++ bool need_suid_safe = false; + bool core_dumped = false; + static atomic_t core_dump_count = ATOMIC_INIT(0); + struct coredump_params cprm = { +@@ -550,9 +550,8 @@ void do_coredump(const siginfo_t *siginfo) + */ + if (__get_dumpable(cprm.mm_flags) == SUID_DUMP_ROOT) { + /* Setuid core dump mode */ +- flag = O_EXCL; /* Stop rewrite attacks */ + cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */ +- need_nonrelative = true; ++ need_suid_safe = true; + } + + retval = coredump_wait(siginfo->si_signo, &core_state); +@@ -633,7 +632,7 @@ void do_coredump(const siginfo_t *siginfo) + if (cprm.limit < binfmt->min_coredump) + goto fail_unlock; + +- if (need_nonrelative && cn.corename[0] != '/') { ++ if (need_suid_safe && cn.corename[0] != '/') { + printk(KERN_WARNING "Pid %d(%s) can only dump core "\ + "to fully qualified path!\n", + task_tgid_vnr(current), current->comm); +@@ -641,8 +640,35 @@ void do_coredump(const siginfo_t *siginfo) + goto fail_unlock; + } + ++ /* ++ * Unlink the file if it exists unless this is a SUID ++ * binary - in that case, we're running around with root ++ * privs and don't want to unlink another user's coredump. ++ */ ++ if (!need_suid_safe) { ++ mm_segment_t old_fs; ++ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ /* ++ * If it doesn't exist, that's fine. If there's some ++ * other problem, we'll catch it at the filp_open(). ++ */ ++ (void) sys_unlink((const char __user *)cn.corename); ++ set_fs(old_fs); ++ } ++ ++ /* ++ * There is a race between unlinking and creating the ++ * file, but if that causes an EEXIST here, that's ++ * fine - another process raced with us while creating ++ * the corefile, and the other process won. To userspace, ++ * what matters is that at least one of the two processes ++ * writes its coredump successfully, not which one. ++ */ + cprm.file = filp_open(cn.corename, +- O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, ++ O_CREAT | 2 | O_NOFOLLOW | ++ O_LARGEFILE | O_EXCL, + 0600); + if (IS_ERR(cprm.file)) + goto fail_unlock; +@@ -659,11 +685,15 @@ void do_coredump(const siginfo_t *siginfo) + if (!S_ISREG(inode->i_mode)) + goto close_fail; + /* +- * Dont allow local users get cute and trick others to coredump +- * into their pre-created files. ++ * Don't dump core if the filesystem changed owner or mode ++ * of the file during file creation. This is an issue when ++ * a process dumps core while its cwd is e.g. on a vfat ++ * filesystem. + */ + if (!uid_eq(inode->i_uid, current_fsuid())) + goto close_fail; ++ if ((inode->i_mode & 0677) != 0600) ++ goto close_fail; + if (!(cprm.file->f_mode & FMODE_CAN_WRITE)) + goto close_fail; + if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file)) +diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c +index 8db0b464483f..63cd2c147221 100644 +--- a/fs/ecryptfs/dentry.c ++++ b/fs/ecryptfs/dentry.c +@@ -45,20 +45,20 @@ + static int ecryptfs_d_revalidate(struct dentry *dentry, unsigned int flags) + { + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); +- int rc; +- +- if (!(lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) +- return 1; ++ int rc = 1; + + if (flags & LOOKUP_RCU) + return -ECHILD; + +- rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags); ++ if (lower_dentry->d_flags & DCACHE_OP_REVALIDATE) ++ rc = lower_dentry->d_op->d_revalidate(lower_dentry, flags); ++ + if (d_really_is_positive(dentry)) { +- struct inode *lower_inode = +- ecryptfs_inode_to_lower(d_inode(dentry)); ++ struct inode *inode = d_inode(dentry); + +- fsstack_copy_attr_all(d_inode(dentry), lower_inode); ++ fsstack_copy_attr_all(inode, ecryptfs_inode_to_lower(inode)); ++ if (!inode->i_nlink) ++ return 0; + } + return rc; + } +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index 9981064c4a54..a5e8c744e962 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -325,6 +325,22 @@ static void save_error_info(struct super_block *sb, const char *func, + ext4_commit_super(sb, 1); + } + ++/* ++ * The del_gendisk() function uninitializes the disk-specific data ++ * structures, including the bdi structure, without telling anyone ++ * else. Once this happens, any attempt to call mark_buffer_dirty() ++ * (for example, by ext4_commit_super), will cause a kernel OOPS. ++ * This is a kludge to prevent these oops until we can put in a proper ++ * hook in del_gendisk() to inform the VFS and file system layers. ++ */ ++static int block_device_ejected(struct super_block *sb) ++{ ++ struct inode *bd_inode = sb->s_bdev->bd_inode; ++ struct backing_dev_info *bdi = inode_to_bdi(bd_inode); ++ ++ return bdi->dev == NULL; ++} ++ + static void ext4_journal_commit_callback(journal_t *journal, transaction_t *txn) + { + struct super_block *sb = journal->j_private; +@@ -4617,7 +4633,7 @@ static int ext4_commit_super(struct super_block *sb, int sync) + struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; + int error = 0; + +- if (!sbh) ++ if (!sbh || block_device_ejected(sb)) + return error; + if (buffer_write_io_error(sbh)) { + /* +@@ -4833,10 +4849,11 @@ static int ext4_freeze(struct super_block *sb) + error = jbd2_journal_flush(journal); + if (error < 0) + goto out; ++ ++ /* Journal blocked and flushed, clear needs_recovery flag. */ ++ EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); + } + +- /* Journal blocked and flushed, clear needs_recovery flag. */ +- EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); + error = ext4_commit_super(sb, 1); + out: + if (journal) +@@ -4854,8 +4871,11 @@ static int ext4_unfreeze(struct super_block *sb) + if (sb->s_flags & MS_RDONLY) + return 0; + +- /* Reset the needs_recovery flag before the fs is unlocked. */ +- EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); ++ if (EXT4_SB(sb)->s_journal) { ++ /* Reset the needs_recovery flag before the fs is unlocked. */ ++ EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); ++ } ++ + ext4_commit_super(sb, 1); + return 0; + } +diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c +index d3fa6bd9503e..221719eac5de 100644 +--- a/fs/hfs/bnode.c ++++ b/fs/hfs/bnode.c +@@ -288,7 +288,6 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) + page_cache_release(page); + goto fail; + } +- page_cache_release(page); + node->page[i] = page; + } + +@@ -398,11 +397,11 @@ node_error: + + void hfs_bnode_free(struct hfs_bnode *node) + { +- //int i; ++ int i; + +- //for (i = 0; i < node->tree->pages_per_bnode; i++) +- // if (node->page[i]) +- // page_cache_release(node->page[i]); ++ for (i = 0; i < node->tree->pages_per_bnode; i++) ++ if (node->page[i]) ++ page_cache_release(node->page[i]); + kfree(node); + } + +diff --git a/fs/hfs/brec.c b/fs/hfs/brec.c +index 9f4ee7f52026..6fc766df0461 100644 +--- a/fs/hfs/brec.c ++++ b/fs/hfs/brec.c +@@ -131,13 +131,16 @@ skip: + hfs_bnode_write(node, entry, data_off + key_len, entry_len); + hfs_bnode_dump(node); + +- if (new_node) { +- /* update parent key if we inserted a key +- * at the start of the first node +- */ +- if (!rec && new_node != node) +- hfs_brec_update_parent(fd); ++ /* ++ * update parent key if we inserted a key ++ * at the start of the node and it is not the new node ++ */ ++ if (!rec && new_node != node) { ++ hfs_bnode_read_key(node, fd->search_key, data_off + size); ++ hfs_brec_update_parent(fd); ++ } + ++ if (new_node) { + hfs_bnode_put(fd->bnode); + if (!new_node->parent) { + hfs_btree_inc_height(tree); +@@ -166,9 +169,6 @@ skip: + goto again; + } + +- if (!rec) +- hfs_brec_update_parent(fd); +- + return 0; + } + +@@ -366,6 +366,8 @@ again: + if (IS_ERR(parent)) + return PTR_ERR(parent); + __hfs_brec_find(parent, fd); ++ if (fd->record < 0) ++ return -ENOENT; + hfs_bnode_dump(parent); + rec = fd->record; + +diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c +index 759708fd9331..63924662aaf3 100644 +--- a/fs/hfsplus/bnode.c ++++ b/fs/hfsplus/bnode.c +@@ -454,7 +454,6 @@ static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid) + page_cache_release(page); + goto fail; + } +- page_cache_release(page); + node->page[i] = page; + } + +@@ -566,13 +565,11 @@ node_error: + + void hfs_bnode_free(struct hfs_bnode *node) + { +-#if 0 + int i; + + for (i = 0; i < node->tree->pages_per_bnode; i++) + if (node->page[i]) + page_cache_release(node->page[i]); +-#endif + kfree(node); + } + +diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c +index 4227dc4f7437..8c44654ce274 100644 +--- a/fs/jbd2/checkpoint.c ++++ b/fs/jbd2/checkpoint.c +@@ -417,12 +417,12 @@ int jbd2_cleanup_journal_tail(journal_t *journal) + * journal_clean_one_cp_list + * + * Find all the written-back checkpoint buffers in the given list and +- * release them. ++ * release them. If 'destroy' is set, clean all buffers unconditionally. + * + * Called with j_list_lock held. + * Returns 1 if we freed the transaction, 0 otherwise. + */ +-static int journal_clean_one_cp_list(struct journal_head *jh) ++static int journal_clean_one_cp_list(struct journal_head *jh, bool destroy) + { + struct journal_head *last_jh; + struct journal_head *next_jh = jh; +@@ -436,7 +436,10 @@ static int journal_clean_one_cp_list(struct journal_head *jh) + do { + jh = next_jh; + next_jh = jh->b_cpnext; +- ret = __try_to_free_cp_buf(jh); ++ if (!destroy) ++ ret = __try_to_free_cp_buf(jh); ++ else ++ ret = __jbd2_journal_remove_checkpoint(jh) + 1; + if (!ret) + return freed; + if (ret == 2) +@@ -459,10 +462,11 @@ static int journal_clean_one_cp_list(struct journal_head *jh) + * journal_clean_checkpoint_list + * + * Find all the written-back checkpoint buffers in the journal and release them. ++ * If 'destroy' is set, release all buffers unconditionally. + * + * Called with j_list_lock held. + */ +-void __jbd2_journal_clean_checkpoint_list(journal_t *journal) ++void __jbd2_journal_clean_checkpoint_list(journal_t *journal, bool destroy) + { + transaction_t *transaction, *last_transaction, *next_transaction; + int ret; +@@ -476,7 +480,8 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal) + do { + transaction = next_transaction; + next_transaction = transaction->t_cpnext; +- ret = journal_clean_one_cp_list(transaction->t_checkpoint_list); ++ ret = journal_clean_one_cp_list(transaction->t_checkpoint_list, ++ destroy); + /* + * This function only frees up some memory if possible so we + * dont have an obligation to finish processing. Bail out if +@@ -492,7 +497,7 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal) + * we can possibly see not yet submitted buffers on io_list + */ + ret = journal_clean_one_cp_list(transaction-> +- t_checkpoint_io_list); ++ t_checkpoint_io_list, destroy); + if (need_resched()) + return; + /* +@@ -506,6 +511,28 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal) + } + + /* ++ * Remove buffers from all checkpoint lists as journal is aborted and we just ++ * need to free memory ++ */ ++void jbd2_journal_destroy_checkpoint(journal_t *journal) ++{ ++ /* ++ * We loop because __jbd2_journal_clean_checkpoint_list() may abort ++ * early due to a need of rescheduling. ++ */ ++ while (1) { ++ spin_lock(&journal->j_list_lock); ++ if (!journal->j_checkpoint_transactions) { ++ spin_unlock(&journal->j_list_lock); ++ break; ++ } ++ __jbd2_journal_clean_checkpoint_list(journal, true); ++ spin_unlock(&journal->j_list_lock); ++ cond_resched(); ++ } ++} ++ ++/* + * journal_remove_checkpoint: called after a buffer has been committed + * to disk (either by being write-back flushed to disk, or being + * committed to the log). +diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c +index b73e0215baa7..362e5f614450 100644 +--- a/fs/jbd2/commit.c ++++ b/fs/jbd2/commit.c +@@ -510,7 +510,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) + * frees some memory + */ + spin_lock(&journal->j_list_lock); +- __jbd2_journal_clean_checkpoint_list(journal); ++ __jbd2_journal_clean_checkpoint_list(journal, false); + spin_unlock(&journal->j_list_lock); + + jbd_debug(3, "JBD2: commit phase 1\n"); +diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c +index 4ff3fad4e9e3..2721513adb1f 100644 +--- a/fs/jbd2/journal.c ++++ b/fs/jbd2/journal.c +@@ -1693,8 +1693,17 @@ int jbd2_journal_destroy(journal_t *journal) + while (journal->j_checkpoint_transactions != NULL) { + spin_unlock(&journal->j_list_lock); + mutex_lock(&journal->j_checkpoint_mutex); +- jbd2_log_do_checkpoint(journal); ++ err = jbd2_log_do_checkpoint(journal); + mutex_unlock(&journal->j_checkpoint_mutex); ++ /* ++ * If checkpointing failed, just free the buffers to avoid ++ * looping forever ++ */ ++ if (err) { ++ jbd2_journal_destroy_checkpoint(journal); ++ spin_lock(&journal->j_list_lock); ++ break; ++ } + spin_lock(&journal->j_list_lock); + } + +diff --git a/fs/nfs/flexfilelayout/flexfilelayout.c b/fs/nfs/flexfilelayout/flexfilelayout.c +index b3289d701eea..14e3b1e1b17d 100644 +--- a/fs/nfs/flexfilelayout/flexfilelayout.c ++++ b/fs/nfs/flexfilelayout/flexfilelayout.c +@@ -1199,6 +1199,11 @@ static int ff_layout_write_done_cb(struct rpc_task *task, + hdr->res.verf->committed == NFS_DATA_SYNC) + ff_layout_set_layoutcommit(hdr); + ++ /* zero out fattr since we don't care DS attr at all */ ++ hdr->fattr.valid = 0; ++ if (task->tk_status >= 0) ++ nfs_writeback_update_inode(hdr); ++ + return 0; + } + +diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c +index f13e1969eedd..b28fa4cbea52 100644 +--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c ++++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c +@@ -500,16 +500,19 @@ int ff_layout_encode_ds_ioerr(struct nfs4_flexfile_layout *flo, + range->offset, range->length)) + continue; + /* offset(8) + length(8) + stateid(NFS4_STATEID_SIZE) +- * + deviceid(NFS4_DEVICEID4_SIZE) + status(4) + opnum(4) ++ * + array length + deviceid(NFS4_DEVICEID4_SIZE) ++ * + status(4) + opnum(4) + */ + p = xdr_reserve_space(xdr, +- 24 + NFS4_STATEID_SIZE + NFS4_DEVICEID4_SIZE); ++ 28 + NFS4_STATEID_SIZE + NFS4_DEVICEID4_SIZE); + if (unlikely(!p)) + return -ENOBUFS; + p = xdr_encode_hyper(p, err->offset); + p = xdr_encode_hyper(p, err->length); + p = xdr_encode_opaque_fixed(p, &err->stateid, + NFS4_STATEID_SIZE); ++ /* Encode 1 error */ ++ *p++ = cpu_to_be32(1); + p = xdr_encode_opaque_fixed(p, &err->deviceid, + NFS4_DEVICEID4_SIZE); + *p++ = cpu_to_be32(err->status); +diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c +index 0adc7d245b3d..4afbe13321cb 100644 +--- a/fs/nfs/inode.c ++++ b/fs/nfs/inode.c +@@ -1273,13 +1273,6 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat + return 0; + } + +-static int nfs_ctime_need_update(const struct inode *inode, const struct nfs_fattr *fattr) +-{ +- if (!(fattr->valid & NFS_ATTR_FATTR_CTIME)) +- return 0; +- return timespec_compare(&fattr->ctime, &inode->i_ctime) > 0; +-} +- + static atomic_long_t nfs_attr_generation_counter; + + static unsigned long nfs_read_attr_generation_counter(void) +@@ -1428,7 +1421,6 @@ static int nfs_inode_attrs_need_update(const struct inode *inode, const struct n + const struct nfs_inode *nfsi = NFS_I(inode); + + return ((long)fattr->gencount - (long)nfsi->attr_gencount) > 0 || +- nfs_ctime_need_update(inode, fattr) || + ((long)nfsi->attr_gencount - (long)nfs_read_attr_generation_counter() > 0); + } + +@@ -1491,6 +1483,13 @@ static int nfs_post_op_update_inode_locked(struct inode *inode, struct nfs_fattr + { + unsigned long invalid = NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; + ++ /* ++ * Don't revalidate the pagecache if we hold a delegation, but do ++ * force an attribute update ++ */ ++ if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) ++ invalid = NFS_INO_INVALID_ATTR|NFS_INO_REVAL_FORCED; ++ + if (S_ISDIR(inode->i_mode)) + invalid |= NFS_INO_INVALID_DATA; + nfs_set_cache_invalid(inode, invalid); +diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h +index 9b372b845f6a..1dad18105ed0 100644 +--- a/fs/nfs/internal.h ++++ b/fs/nfs/internal.h +@@ -490,6 +490,9 @@ void nfs_retry_commit(struct list_head *page_list, + void nfs_commitdata_release(struct nfs_commit_data *data); + void nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst, + struct nfs_commit_info *cinfo); ++void nfs_request_add_commit_list_locked(struct nfs_page *req, ++ struct list_head *dst, ++ struct nfs_commit_info *cinfo); + void nfs_request_remove_commit_list(struct nfs_page *req, + struct nfs_commit_info *cinfo); + void nfs_init_cinfo(struct nfs_commit_info *cinfo, +@@ -623,13 +626,15 @@ void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) + * Record the page as unstable and mark its inode as dirty. + */ + static inline +-void nfs_mark_page_unstable(struct page *page) ++void nfs_mark_page_unstable(struct page *page, struct nfs_commit_info *cinfo) + { +- struct inode *inode = page_file_mapping(page)->host; ++ if (!cinfo->dreq) { ++ struct inode *inode = page_file_mapping(page)->host; + +- inc_zone_page_state(page, NR_UNSTABLE_NFS); +- inc_wb_stat(&inode_to_bdi(inode)->wb, WB_RECLAIMABLE); +- __mark_inode_dirty(inode, I_DIRTY_DATASYNC); ++ inc_zone_page_state(page, NR_UNSTABLE_NFS); ++ inc_wb_stat(&inode_to_bdi(inode)->wb, WB_RECLAIMABLE); ++ __mark_inode_dirty(inode, I_DIRTY_DATASYNC); ++ } + } + + /* +diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c +index 3acb1eb72930..73c8204ad463 100644 +--- a/fs/nfs/nfs4proc.c ++++ b/fs/nfs/nfs4proc.c +@@ -1156,6 +1156,8 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode) + return 0; + if ((delegation->type & fmode) != fmode) + return 0; ++ if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags)) ++ return 0; + if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) + return 0; + nfs_mark_delegation_referenced(delegation); +@@ -1220,6 +1222,7 @@ static void nfs_resync_open_stateid_locked(struct nfs4_state *state) + } + + static void nfs_clear_open_stateid_locked(struct nfs4_state *state, ++ nfs4_stateid *arg_stateid, + nfs4_stateid *stateid, fmode_t fmode) + { + clear_bit(NFS_O_RDWR_STATE, &state->flags); +@@ -1238,8 +1241,9 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state, + if (stateid == NULL) + return; + /* Handle races with OPEN */ +- if (!nfs4_stateid_match_other(stateid, &state->open_stateid) || +- !nfs4_stateid_is_newer(stateid, &state->open_stateid)) { ++ if (!nfs4_stateid_match_other(arg_stateid, &state->open_stateid) || ++ (nfs4_stateid_match_other(stateid, &state->open_stateid) && ++ !nfs4_stateid_is_newer(stateid, &state->open_stateid))) { + nfs_resync_open_stateid_locked(state); + return; + } +@@ -1248,10 +1252,12 @@ static void nfs_clear_open_stateid_locked(struct nfs4_state *state, + nfs4_stateid_copy(&state->open_stateid, stateid); + } + +-static void nfs_clear_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode) ++static void nfs_clear_open_stateid(struct nfs4_state *state, ++ nfs4_stateid *arg_stateid, ++ nfs4_stateid *stateid, fmode_t fmode) + { + write_seqlock(&state->seqlock); +- nfs_clear_open_stateid_locked(state, stateid, fmode); ++ nfs_clear_open_stateid_locked(state, arg_stateid, stateid, fmode); + write_sequnlock(&state->seqlock); + if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) + nfs4_schedule_state_manager(state->owner->so_server->nfs_client); +@@ -2425,7 +2431,7 @@ static int _nfs4_do_open(struct inode *dir, + goto err_free_label; + state = ctx->state; + +- if ((opendata->o_arg.open_flags & O_EXCL) && ++ if ((opendata->o_arg.open_flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) && + (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) { + nfs4_exclusive_attrset(opendata, sattr); + +@@ -2684,7 +2690,8 @@ static void nfs4_close_done(struct rpc_task *task, void *data) + goto out_release; + } + } +- nfs_clear_open_stateid(state, res_stateid, calldata->arg.fmode); ++ nfs_clear_open_stateid(state, &calldata->arg.stateid, ++ res_stateid, calldata->arg.fmode); + out_release: + nfs_release_seqid(calldata->arg.seqid); + nfs_refresh_inode(calldata->inode, calldata->res.fattr); +@@ -4984,7 +4991,7 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp) + return 0; + retry: + rcu_read_lock(); +- len = 10 + strlen(clp->cl_ipaddr) + 1 + ++ len = 14 + strlen(clp->cl_ipaddr) + 1 + + strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) + + 1 + + strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_PROTO)) + +@@ -8661,6 +8668,7 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = { + .reboot_recovery_ops = &nfs41_reboot_recovery_ops, + .nograce_recovery_ops = &nfs41_nograce_recovery_ops, + .state_renewal_ops = &nfs41_state_renewal_ops, ++ .mig_recovery_ops = &nfs41_mig_recovery_ops, + }; + #endif + +diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c +index 4984bbe55ff1..7c5718ba625e 100644 +--- a/fs/nfs/pagelist.c ++++ b/fs/nfs/pagelist.c +@@ -77,8 +77,8 @@ EXPORT_SYMBOL_GPL(nfs_pgheader_init); + void nfs_set_pgio_error(struct nfs_pgio_header *hdr, int error, loff_t pos) + { + spin_lock(&hdr->lock); +- if (pos < hdr->io_start + hdr->good_bytes) { +- set_bit(NFS_IOHDR_ERROR, &hdr->flags); ++ if (!test_and_set_bit(NFS_IOHDR_ERROR, &hdr->flags) ++ || pos < hdr->io_start + hdr->good_bytes) { + clear_bit(NFS_IOHDR_EOF, &hdr->flags); + hdr->good_bytes = pos - hdr->io_start; + hdr->error = error; +diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c +index f37e25b6311c..e5c679f04099 100644 +--- a/fs/nfs/pnfs_nfs.c ++++ b/fs/nfs/pnfs_nfs.c +@@ -359,26 +359,31 @@ same_sockaddr(struct sockaddr *addr1, struct sockaddr *addr2) + return false; + } + ++/* ++ * Checks if 'dsaddrs1' contains a subset of 'dsaddrs2'. If it does, ++ * declare a match. ++ */ + static bool + _same_data_server_addrs_locked(const struct list_head *dsaddrs1, + const struct list_head *dsaddrs2) + { + struct nfs4_pnfs_ds_addr *da1, *da2; +- +- /* step through both lists, comparing as we go */ +- for (da1 = list_first_entry(dsaddrs1, typeof(*da1), da_node), +- da2 = list_first_entry(dsaddrs2, typeof(*da2), da_node); +- da1 != NULL && da2 != NULL; +- da1 = list_entry(da1->da_node.next, typeof(*da1), da_node), +- da2 = list_entry(da2->da_node.next, typeof(*da2), da_node)) { +- if (!same_sockaddr((struct sockaddr *)&da1->da_addr, +- (struct sockaddr *)&da2->da_addr)) +- return false; ++ struct sockaddr *sa1, *sa2; ++ bool match = false; ++ ++ list_for_each_entry(da1, dsaddrs1, da_node) { ++ sa1 = (struct sockaddr *)&da1->da_addr; ++ match = false; ++ list_for_each_entry(da2, dsaddrs2, da_node) { ++ sa2 = (struct sockaddr *)&da2->da_addr; ++ match = same_sockaddr(sa1, sa2); ++ if (match) ++ break; ++ } ++ if (!match) ++ break; + } +- if (da1 == NULL && da2 == NULL) +- return true; +- +- return false; ++ return match; + } + + /* +@@ -863,9 +868,10 @@ pnfs_layout_mark_request_commit(struct nfs_page *req, + } + set_bit(PG_COMMIT_TO_DS, &req->wb_flags); + cinfo->ds->nwritten++; +- spin_unlock(cinfo->lock); + +- nfs_request_add_commit_list(req, list, cinfo); ++ nfs_request_add_commit_list_locked(req, list, cinfo); ++ spin_unlock(cinfo->lock); ++ nfs_mark_page_unstable(req->wb_page, cinfo); + } + EXPORT_SYMBOL_GPL(pnfs_layout_mark_request_commit); + +diff --git a/fs/nfs/write.c b/fs/nfs/write.c +index 75a35a1afa79..fdee9270ca15 100644 +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -768,6 +768,28 @@ nfs_page_search_commits_for_head_request_locked(struct nfs_inode *nfsi, + } + + /** ++ * nfs_request_add_commit_list_locked - add request to a commit list ++ * @req: pointer to a struct nfs_page ++ * @dst: commit list head ++ * @cinfo: holds list lock and accounting info ++ * ++ * This sets the PG_CLEAN bit, updates the cinfo count of ++ * number of outstanding requests requiring a commit as well as ++ * the MM page stats. ++ * ++ * The caller must hold the cinfo->lock, and the nfs_page lock. ++ */ ++void ++nfs_request_add_commit_list_locked(struct nfs_page *req, struct list_head *dst, ++ struct nfs_commit_info *cinfo) ++{ ++ set_bit(PG_CLEAN, &req->wb_flags); ++ nfs_list_add_request(req, dst); ++ cinfo->mds->ncommit++; ++} ++EXPORT_SYMBOL_GPL(nfs_request_add_commit_list_locked); ++ ++/** + * nfs_request_add_commit_list - add request to a commit list + * @req: pointer to a struct nfs_page + * @dst: commit list head +@@ -784,13 +806,10 @@ void + nfs_request_add_commit_list(struct nfs_page *req, struct list_head *dst, + struct nfs_commit_info *cinfo) + { +- set_bit(PG_CLEAN, &(req)->wb_flags); + spin_lock(cinfo->lock); +- nfs_list_add_request(req, dst); +- cinfo->mds->ncommit++; ++ nfs_request_add_commit_list_locked(req, dst, cinfo); + spin_unlock(cinfo->lock); +- if (!cinfo->dreq) +- nfs_mark_page_unstable(req->wb_page); ++ nfs_mark_page_unstable(req->wb_page, cinfo); + } + EXPORT_SYMBOL_GPL(nfs_request_add_commit_list); + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 95202719a1fd..75189cd34583 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -777,13 +777,16 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp) + list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations); + } + +-static void ++static bool + unhash_delegation_locked(struct nfs4_delegation *dp) + { + struct nfs4_file *fp = dp->dl_stid.sc_file; + + lockdep_assert_held(&state_lock); + ++ if (list_empty(&dp->dl_perfile)) ++ return false; ++ + dp->dl_stid.sc_type = NFS4_CLOSED_DELEG_STID; + /* Ensure that deleg break won't try to requeue it */ + ++dp->dl_time; +@@ -792,16 +795,21 @@ unhash_delegation_locked(struct nfs4_delegation *dp) + list_del_init(&dp->dl_recall_lru); + list_del_init(&dp->dl_perfile); + spin_unlock(&fp->fi_lock); ++ return true; + } + + static void destroy_delegation(struct nfs4_delegation *dp) + { ++ bool unhashed; ++ + spin_lock(&state_lock); +- unhash_delegation_locked(dp); ++ unhashed = unhash_delegation_locked(dp); + spin_unlock(&state_lock); +- put_clnt_odstate(dp->dl_clnt_odstate); +- nfs4_put_deleg_lease(dp->dl_stid.sc_file); +- nfs4_put_stid(&dp->dl_stid); ++ if (unhashed) { ++ put_clnt_odstate(dp->dl_clnt_odstate); ++ nfs4_put_deleg_lease(dp->dl_stid.sc_file); ++ nfs4_put_stid(&dp->dl_stid); ++ } + } + + static void revoke_delegation(struct nfs4_delegation *dp) +@@ -1004,16 +1012,20 @@ static void nfs4_put_stateowner(struct nfs4_stateowner *sop) + sop->so_ops->so_free(sop); + } + +-static void unhash_ol_stateid(struct nfs4_ol_stateid *stp) ++static bool unhash_ol_stateid(struct nfs4_ol_stateid *stp) + { + struct nfs4_file *fp = stp->st_stid.sc_file; + + lockdep_assert_held(&stp->st_stateowner->so_client->cl_lock); + ++ if (list_empty(&stp->st_perfile)) ++ return false; ++ + spin_lock(&fp->fi_lock); +- list_del(&stp->st_perfile); ++ list_del_init(&stp->st_perfile); + spin_unlock(&fp->fi_lock); + list_del(&stp->st_perstateowner); ++ return true; + } + + static void nfs4_free_ol_stateid(struct nfs4_stid *stid) +@@ -1063,25 +1075,27 @@ static void put_ol_stateid_locked(struct nfs4_ol_stateid *stp, + list_add(&stp->st_locks, reaplist); + } + +-static void unhash_lock_stateid(struct nfs4_ol_stateid *stp) ++static bool unhash_lock_stateid(struct nfs4_ol_stateid *stp) + { + struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner); + + lockdep_assert_held(&oo->oo_owner.so_client->cl_lock); + + list_del_init(&stp->st_locks); +- unhash_ol_stateid(stp); + nfs4_unhash_stid(&stp->st_stid); ++ return unhash_ol_stateid(stp); + } + + static void release_lock_stateid(struct nfs4_ol_stateid *stp) + { + struct nfs4_openowner *oo = openowner(stp->st_openstp->st_stateowner); ++ bool unhashed; + + spin_lock(&oo->oo_owner.so_client->cl_lock); +- unhash_lock_stateid(stp); ++ unhashed = unhash_lock_stateid(stp); + spin_unlock(&oo->oo_owner.so_client->cl_lock); +- nfs4_put_stid(&stp->st_stid); ++ if (unhashed) ++ nfs4_put_stid(&stp->st_stid); + } + + static void unhash_lockowner_locked(struct nfs4_lockowner *lo) +@@ -1129,7 +1143,7 @@ static void release_lockowner(struct nfs4_lockowner *lo) + while (!list_empty(&lo->lo_owner.so_stateids)) { + stp = list_first_entry(&lo->lo_owner.so_stateids, + struct nfs4_ol_stateid, st_perstateowner); +- unhash_lock_stateid(stp); ++ WARN_ON(!unhash_lock_stateid(stp)); + put_ol_stateid_locked(stp, &reaplist); + } + spin_unlock(&clp->cl_lock); +@@ -1142,21 +1156,26 @@ static void release_open_stateid_locks(struct nfs4_ol_stateid *open_stp, + { + struct nfs4_ol_stateid *stp; + ++ lockdep_assert_held(&open_stp->st_stid.sc_client->cl_lock); ++ + while (!list_empty(&open_stp->st_locks)) { + stp = list_entry(open_stp->st_locks.next, + struct nfs4_ol_stateid, st_locks); +- unhash_lock_stateid(stp); ++ WARN_ON(!unhash_lock_stateid(stp)); + put_ol_stateid_locked(stp, reaplist); + } + } + +-static void unhash_open_stateid(struct nfs4_ol_stateid *stp, ++static bool unhash_open_stateid(struct nfs4_ol_stateid *stp, + struct list_head *reaplist) + { ++ bool unhashed; ++ + lockdep_assert_held(&stp->st_stid.sc_client->cl_lock); + +- unhash_ol_stateid(stp); ++ unhashed = unhash_ol_stateid(stp); + release_open_stateid_locks(stp, reaplist); ++ return unhashed; + } + + static void release_open_stateid(struct nfs4_ol_stateid *stp) +@@ -1164,8 +1183,8 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp) + LIST_HEAD(reaplist); + + spin_lock(&stp->st_stid.sc_client->cl_lock); +- unhash_open_stateid(stp, &reaplist); +- put_ol_stateid_locked(stp, &reaplist); ++ if (unhash_open_stateid(stp, &reaplist)) ++ put_ol_stateid_locked(stp, &reaplist); + spin_unlock(&stp->st_stid.sc_client->cl_lock); + free_ol_stateid_reaplist(&reaplist); + } +@@ -1210,8 +1229,8 @@ static void release_openowner(struct nfs4_openowner *oo) + while (!list_empty(&oo->oo_owner.so_stateids)) { + stp = list_first_entry(&oo->oo_owner.so_stateids, + struct nfs4_ol_stateid, st_perstateowner); +- unhash_open_stateid(stp, &reaplist); +- put_ol_stateid_locked(stp, &reaplist); ++ if (unhash_open_stateid(stp, &reaplist)) ++ put_ol_stateid_locked(stp, &reaplist); + } + spin_unlock(&clp->cl_lock); + free_ol_stateid_reaplist(&reaplist); +@@ -1714,7 +1733,7 @@ __destroy_client(struct nfs4_client *clp) + spin_lock(&state_lock); + while (!list_empty(&clp->cl_delegations)) { + dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt); +- unhash_delegation_locked(dp); ++ WARN_ON(!unhash_delegation_locked(dp)); + list_add(&dp->dl_recall_lru, &reaplist); + } + spin_unlock(&state_lock); +@@ -4345,7 +4364,7 @@ nfs4_laundromat(struct nfsd_net *nn) + new_timeo = min(new_timeo, t); + break; + } +- unhash_delegation_locked(dp); ++ WARN_ON(!unhash_delegation_locked(dp)); + list_add(&dp->dl_recall_lru, &reaplist); + } + spin_unlock(&state_lock); +@@ -4751,7 +4770,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, + if (check_for_locks(stp->st_stid.sc_file, + lockowner(stp->st_stateowner))) + break; +- unhash_lock_stateid(stp); ++ WARN_ON(!unhash_lock_stateid(stp)); + spin_unlock(&cl->cl_lock); + nfs4_put_stid(s); + ret = nfs_ok; +@@ -4967,20 +4986,23 @@ out: + static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) + { + struct nfs4_client *clp = s->st_stid.sc_client; ++ bool unhashed; + LIST_HEAD(reaplist); + + s->st_stid.sc_type = NFS4_CLOSED_STID; + spin_lock(&clp->cl_lock); +- unhash_open_stateid(s, &reaplist); ++ unhashed = unhash_open_stateid(s, &reaplist); + + if (clp->cl_minorversion) { +- put_ol_stateid_locked(s, &reaplist); ++ if (unhashed) ++ put_ol_stateid_locked(s, &reaplist); + spin_unlock(&clp->cl_lock); + free_ol_stateid_reaplist(&reaplist); + } else { + spin_unlock(&clp->cl_lock); + free_ol_stateid_reaplist(&reaplist); +- move_to_close_lru(s, clp->net); ++ if (unhashed) ++ move_to_close_lru(s, clp->net); + } + } + +@@ -6019,7 +6041,7 @@ nfsd_inject_add_lock_to_list(struct nfs4_ol_stateid *lst, + + static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, + struct list_head *collect, +- void (*func)(struct nfs4_ol_stateid *)) ++ bool (*func)(struct nfs4_ol_stateid *)) + { + struct nfs4_openowner *oop; + struct nfs4_ol_stateid *stp, *st_next; +@@ -6033,9 +6055,9 @@ static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, + list_for_each_entry_safe(lst, lst_next, + &stp->st_locks, st_locks) { + if (func) { +- func(lst); +- nfsd_inject_add_lock_to_list(lst, +- collect); ++ if (func(lst)) ++ nfsd_inject_add_lock_to_list(lst, ++ collect); + } + ++count; + /* +@@ -6305,7 +6327,7 @@ static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, + continue; + + atomic_inc(&clp->cl_refcount); +- unhash_delegation_locked(dp); ++ WARN_ON(!unhash_delegation_locked(dp)); + list_add(&dp->dl_recall_lru, victims); + } + ++count; +@@ -6635,7 +6657,7 @@ nfs4_state_shutdown_net(struct net *net) + spin_lock(&state_lock); + list_for_each_safe(pos, next, &nn->del_recall_lru) { + dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); +- unhash_delegation_locked(dp); ++ WARN_ON(!unhash_delegation_locked(dp)); + list_add(&dp->dl_recall_lru, &reaplist); + } + spin_unlock(&state_lock); +diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c +index 75e0563c09d1..b81f725ee21d 100644 +--- a/fs/nfsd/nfs4xdr.c ++++ b/fs/nfsd/nfs4xdr.c +@@ -2140,6 +2140,27 @@ nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp, + return nfsd4_encode_user(xdr, rqstp, ace->who_uid); + } + ++static inline __be32 ++nfsd4_encode_layout_type(struct xdr_stream *xdr, enum pnfs_layouttype layout_type) ++{ ++ __be32 *p; ++ ++ if (layout_type) { ++ p = xdr_reserve_space(xdr, 8); ++ if (!p) ++ return nfserr_resource; ++ *p++ = cpu_to_be32(1); ++ *p++ = cpu_to_be32(layout_type); ++ } else { ++ p = xdr_reserve_space(xdr, 4); ++ if (!p) ++ return nfserr_resource; ++ *p++ = cpu_to_be32(0); ++ } ++ ++ return 0; ++} ++ + #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \ + FATTR4_WORD0_RDATTR_ERROR) + #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID +@@ -2688,20 +2709,16 @@ out_acl: + p = xdr_encode_hyper(p, stat.ino); + } + #ifdef CONFIG_NFSD_PNFS +- if ((bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) || +- (bmval2 & FATTR4_WORD2_LAYOUT_TYPES)) { +- if (exp->ex_layout_type) { +- p = xdr_reserve_space(xdr, 8); +- if (!p) +- goto out_resource; +- *p++ = cpu_to_be32(1); +- *p++ = cpu_to_be32(exp->ex_layout_type); +- } else { +- p = xdr_reserve_space(xdr, 4); +- if (!p) +- goto out_resource; +- *p++ = cpu_to_be32(0); +- } ++ if (bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) { ++ status = nfsd4_encode_layout_type(xdr, exp->ex_layout_type); ++ if (status) ++ goto out; ++ } ++ ++ if (bmval2 & FATTR4_WORD2_LAYOUT_TYPES) { ++ status = nfsd4_encode_layout_type(xdr, exp->ex_layout_type); ++ if (status) ++ goto out; + } + + if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) { +diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h +index edb640ae9a94..eb1cebed3f36 100644 +--- a/include/linux/jbd2.h ++++ b/include/linux/jbd2.h +@@ -1042,8 +1042,9 @@ void jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block); + extern void jbd2_journal_commit_transaction(journal_t *); + + /* Checkpoint list management */ +-void __jbd2_journal_clean_checkpoint_list(journal_t *journal); ++void __jbd2_journal_clean_checkpoint_list(journal_t *journal, bool destroy); + int __jbd2_journal_remove_checkpoint(struct journal_head *); ++void jbd2_journal_destroy_checkpoint(journal_t *journal); + void __jbd2_journal_insert_checkpoint(struct journal_head *, transaction_t *); + + +diff --git a/include/linux/platform_data/st_nci.h b/include/linux/platform_data/st_nci.h +deleted file mode 100644 +index d9d400a297bd..000000000000 +--- a/include/linux/platform_data/st_nci.h ++++ /dev/null +@@ -1,29 +0,0 @@ +-/* +- * Driver include for ST NCI NFC chip family. +- * +- * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms and conditions of the GNU General Public License, +- * version 2, as published by the Free Software Foundation. +- * +- * 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, see . +- */ +- +-#ifndef _ST_NCI_H_ +-#define _ST_NCI_H_ +- +-#define ST_NCI_DRIVER_NAME "st_nci" +- +-struct st_nci_nfc_platform_data { +- unsigned int gpio_reset; +- unsigned int irq_polarity; +-}; +- +-#endif /* _ST_NCI_H_ */ +diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h +index cb94ee4181d4..4929a8a9fd52 100644 +--- a/include/linux/sunrpc/svc_rdma.h ++++ b/include/linux/sunrpc/svc_rdma.h +@@ -172,13 +172,6 @@ struct svcxprt_rdma { + #define RDMAXPRT_SQ_PENDING 2 + #define RDMAXPRT_CONN_PENDING 3 + +-#define RPCRDMA_MAX_SVC_SEGS (64) /* server max scatter/gather */ +-#if RPCSVC_MAXPAYLOAD < (RPCRDMA_MAX_SVC_SEGS << PAGE_SHIFT) +-#define RPCRDMA_MAXPAYLOAD RPCSVC_MAXPAYLOAD +-#else +-#define RPCRDMA_MAXPAYLOAD (RPCRDMA_MAX_SVC_SEGS << PAGE_SHIFT) +-#endif +- + #define RPCRDMA_LISTEN_BACKLOG 10 + /* The default ORD value is based on two outstanding full-size writes with a + * page size of 4k, or 32k * 2 ops / 4k = 16 outstanding RDMA_READ. */ +@@ -187,6 +180,8 @@ struct svcxprt_rdma { + #define RPCRDMA_MAX_REQUESTS 32 + #define RPCRDMA_MAX_REQ_SIZE 4096 + ++#define RPCSVC_MAXPAYLOAD_RDMA RPCSVC_MAXPAYLOAD ++ + /* svc_rdma_marshal.c */ + extern int svc_rdma_xdr_decode_req(struct rpcrdma_msg **, struct svc_rqst *); + extern int svc_rdma_xdr_encode_error(struct svcxprt_rdma *, +diff --git a/include/linux/sunrpc/xprtsock.h b/include/linux/sunrpc/xprtsock.h +index 7591788e9fbf..357e44c1a46b 100644 +--- a/include/linux/sunrpc/xprtsock.h ++++ b/include/linux/sunrpc/xprtsock.h +@@ -42,6 +42,7 @@ struct sock_xprt { + /* + * Connection of transports + */ ++ unsigned long sock_state; + struct delayed_work connect_worker; + struct sockaddr_storage srcaddr; + unsigned short srcport; +@@ -76,6 +77,8 @@ struct sock_xprt { + */ + #define TCP_RPC_REPLY (1UL << 6) + ++#define XPRT_SOCK_CONNECTING 1U ++ + #endif /* __KERNEL__ */ + + #endif /* _LINUX_SUNRPC_XPRTSOCK_H */ +diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h +index 1ab2813273cd..bf2058690ceb 100644 +--- a/include/soc/tegra/mc.h ++++ b/include/soc/tegra/mc.h +@@ -66,6 +66,7 @@ struct tegra_smmu_soc { + bool supports_round_robin_arbitration; + bool supports_request_limit; + ++ unsigned int num_tlb_lines; + unsigned int num_asids; + + const struct tegra_smmu_ops *ops; +diff --git a/include/sound/hda_i915.h b/include/sound/hda_i915.h +index adb5ba5cbd9d..ff99140831ba 100644 +--- a/include/sound/hda_i915.h ++++ b/include/sound/hda_i915.h +@@ -11,7 +11,7 @@ int snd_hdac_get_display_clk(struct hdac_bus *bus); + int snd_hdac_i915_init(struct hdac_bus *bus); + int snd_hdac_i915_exit(struct hdac_bus *bus); + #else +-static int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) ++static inline int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable) + { + return 0; + } +diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h +index fd1a02cb3c82..003dca933803 100644 +--- a/include/trace/events/sunrpc.h ++++ b/include/trace/events/sunrpc.h +@@ -529,18 +529,21 @@ TRACE_EVENT(svc_xprt_do_enqueue, + + TP_STRUCT__entry( + __field(struct svc_xprt *, xprt) +- __field(struct svc_rqst *, rqst) ++ __field_struct(struct sockaddr_storage, ss) ++ __field(int, pid) ++ __field(unsigned long, flags) + ), + + TP_fast_assign( + __entry->xprt = xprt; +- __entry->rqst = rqst; ++ xprt ? memcpy(&__entry->ss, &xprt->xpt_remote, sizeof(__entry->ss)) : memset(&__entry->ss, 0, sizeof(__entry->ss)); ++ __entry->pid = rqst? rqst->rq_task->pid : 0; ++ __entry->flags = xprt ? xprt->xpt_flags : 0; + ), + + TP_printk("xprt=0x%p addr=%pIScp pid=%d flags=%s", __entry->xprt, +- (struct sockaddr *)&__entry->xprt->xpt_remote, +- __entry->rqst ? __entry->rqst->rq_task->pid : 0, +- show_svc_xprt_flags(__entry->xprt->xpt_flags)) ++ (struct sockaddr *)&__entry->ss, ++ __entry->pid, show_svc_xprt_flags(__entry->flags)) + ); + + TRACE_EVENT(svc_xprt_dequeue, +@@ -589,16 +592,20 @@ TRACE_EVENT(svc_handle_xprt, + TP_STRUCT__entry( + __field(struct svc_xprt *, xprt) + __field(int, len) ++ __field_struct(struct sockaddr_storage, ss) ++ __field(unsigned long, flags) + ), + + TP_fast_assign( + __entry->xprt = xprt; ++ xprt ? memcpy(&__entry->ss, &xprt->xpt_remote, sizeof(__entry->ss)) : memset(&__entry->ss, 0, sizeof(__entry->ss)); + __entry->len = len; ++ __entry->flags = xprt ? xprt->xpt_flags : 0; + ), + + TP_printk("xprt=0x%p addr=%pIScp len=%d flags=%s", __entry->xprt, +- (struct sockaddr *)&__entry->xprt->xpt_remote, __entry->len, +- show_svc_xprt_flags(__entry->xprt->xpt_flags)) ++ (struct sockaddr *)&__entry->ss, ++ __entry->len, show_svc_xprt_flags(__entry->flags)) + ); + #endif /* _TRACE_SUNRPC_H */ + +diff --git a/kernel/fork.c b/kernel/fork.c +index dbd9b8d7b7cc..26a70dc7a915 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1871,13 +1871,21 @@ static int check_unshare_flags(unsigned long unshare_flags) + CLONE_NEWUSER|CLONE_NEWPID)) + return -EINVAL; + /* +- * Not implemented, but pretend it works if there is nothing to +- * unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND +- * needs to unshare vm. ++ * Not implemented, but pretend it works if there is nothing ++ * to unshare. Note that unsharing the address space or the ++ * signal handlers also need to unshare the signal queues (aka ++ * CLONE_THREAD). + */ + if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) { +- /* FIXME: get_task_mm() increments ->mm_users */ +- if (atomic_read(¤t->mm->mm_users) > 1) ++ if (!thread_group_empty(current)) ++ return -EINVAL; ++ } ++ if (unshare_flags & (CLONE_SIGHAND | CLONE_VM)) { ++ if (atomic_read(¤t->sighand->count) > 1) ++ return -EINVAL; ++ } ++ if (unshare_flags & CLONE_VM) { ++ if (!current_is_single_threaded()) + return -EINVAL; + } + +@@ -1946,16 +1954,16 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags) + if (unshare_flags & CLONE_NEWUSER) + unshare_flags |= CLONE_THREAD | CLONE_FS; + /* +- * If unsharing a thread from a thread group, must also unshare vm. +- */ +- if (unshare_flags & CLONE_THREAD) +- unshare_flags |= CLONE_VM; +- /* + * If unsharing vm, must also unshare signal handlers. + */ + if (unshare_flags & CLONE_VM) + unshare_flags |= CLONE_SIGHAND; + /* ++ * If unsharing a signal handlers, must also unshare the signal queues. ++ */ ++ if (unshare_flags & CLONE_SIGHAND) ++ unshare_flags |= CLONE_THREAD; ++ /* + * If unsharing namespace, must also unshare filesystem information. + */ + if (unshare_flags & CLONE_NEWNS) +diff --git a/kernel/workqueue.c b/kernel/workqueue.c +index 4c4f06176f74..a413acb59a07 100644 +--- a/kernel/workqueue.c ++++ b/kernel/workqueue.c +@@ -2614,7 +2614,7 @@ void flush_workqueue(struct workqueue_struct *wq) + out_unlock: + mutex_unlock(&wq->mutex); + } +-EXPORT_SYMBOL_GPL(flush_workqueue); ++EXPORT_SYMBOL(flush_workqueue); + + /** + * drain_workqueue - drain a workqueue +diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c +index 6dd0335ea61b..0234361b24b8 100644 +--- a/lib/decompress_bunzip2.c ++++ b/lib/decompress_bunzip2.c +@@ -743,12 +743,12 @@ exit_0: + } + + #ifdef PREBOOT +-STATIC int INIT decompress(unsigned char *buf, long len, ++STATIC int INIT __decompress(unsigned char *buf, long len, + long (*fill)(void*, unsigned long), + long (*flush)(void*, unsigned long), +- unsigned char *outbuf, ++ unsigned char *outbuf, long olen, + long *pos, +- void(*error)(char *x)) ++ void (*error)(char *x)) + { + return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error); + } +diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c +index d4c7891635ec..555c06bf20da 100644 +--- a/lib/decompress_inflate.c ++++ b/lib/decompress_inflate.c +@@ -1,4 +1,5 @@ + #ifdef STATIC ++#define PREBOOT + /* Pre-boot environment: included */ + + /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots +@@ -33,23 +34,23 @@ static long INIT nofill(void *buffer, unsigned long len) + } + + /* Included from initramfs et al code */ +-STATIC int INIT gunzip(unsigned char *buf, long len, ++STATIC int INIT __gunzip(unsigned char *buf, long len, + long (*fill)(void*, unsigned long), + long (*flush)(void*, unsigned long), +- unsigned char *out_buf, ++ unsigned char *out_buf, long out_len, + long *pos, + void(*error)(char *x)) { + u8 *zbuf; + struct z_stream_s *strm; + int rc; +- size_t out_len; + + rc = -1; + if (flush) { + out_len = 0x8000; /* 32 K */ + out_buf = malloc(out_len); + } else { +- out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */ ++ if (!out_len) ++ out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */ + } + if (!out_buf) { + error("Out of memory while allocating output buffer"); +@@ -181,4 +182,24 @@ gunzip_nomem1: + return rc; /* returns Z_OK (0) if successful */ + } + +-#define decompress gunzip ++#ifndef PREBOOT ++STATIC int INIT gunzip(unsigned char *buf, long len, ++ long (*fill)(void*, unsigned long), ++ long (*flush)(void*, unsigned long), ++ unsigned char *out_buf, ++ long *pos, ++ void (*error)(char *x)) ++{ ++ return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error); ++} ++#else ++STATIC int INIT __decompress(unsigned char *buf, long len, ++ long (*fill)(void*, unsigned long), ++ long (*flush)(void*, unsigned long), ++ unsigned char *out_buf, long out_len, ++ long *pos, ++ void (*error)(char *x)) ++{ ++ return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error); ++} ++#endif +diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c +index 40f66ebe57b7..036fc882cd72 100644 +--- a/lib/decompress_unlz4.c ++++ b/lib/decompress_unlz4.c +@@ -196,12 +196,12 @@ exit_0: + } + + #ifdef PREBOOT +-STATIC int INIT decompress(unsigned char *buf, long in_len, ++STATIC int INIT __decompress(unsigned char *buf, long in_len, + long (*fill)(void*, unsigned long), + long (*flush)(void*, unsigned long), +- unsigned char *output, ++ unsigned char *output, long out_len, + long *posp, +- void(*error)(char *x) ++ void (*error)(char *x) + ) + { + return unlz4(buf, in_len - 4, fill, flush, output, posp, error); +diff --git a/lib/decompress_unlzma.c b/lib/decompress_unlzma.c +index 0be83af62b88..decb64629c14 100644 +--- a/lib/decompress_unlzma.c ++++ b/lib/decompress_unlzma.c +@@ -667,13 +667,12 @@ exit_0: + } + + #ifdef PREBOOT +-STATIC int INIT decompress(unsigned char *buf, long in_len, ++STATIC int INIT __decompress(unsigned char *buf, long in_len, + long (*fill)(void*, unsigned long), + long (*flush)(void*, unsigned long), +- unsigned char *output, ++ unsigned char *output, long out_len, + long *posp, +- void(*error)(char *x) +- ) ++ void (*error)(char *x)) + { + return unlzma(buf, in_len - 4, fill, flush, output, posp, error); + } +diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c +index b94a31bdd87d..f4c158e3a022 100644 +--- a/lib/decompress_unlzo.c ++++ b/lib/decompress_unlzo.c +@@ -31,6 +31,7 @@ + */ + + #ifdef STATIC ++#define PREBOOT + #include "lzo/lzo1x_decompress_safe.c" + #else + #include +@@ -287,4 +288,14 @@ exit: + return ret; + } + +-#define decompress unlzo ++#ifdef PREBOOT ++STATIC int INIT __decompress(unsigned char *buf, long len, ++ long (*fill)(void*, unsigned long), ++ long (*flush)(void*, unsigned long), ++ unsigned char *out_buf, long olen, ++ long *pos, ++ void (*error)(char *x)) ++{ ++ return unlzo(buf, len, fill, flush, out_buf, pos, error); ++} ++#endif +diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c +index b07a78340e9d..25d59a95bd66 100644 +--- a/lib/decompress_unxz.c ++++ b/lib/decompress_unxz.c +@@ -394,4 +394,14 @@ error_alloc_state: + * This macro is used by architecture-specific files to decompress + * the kernel image. + */ +-#define decompress unxz ++#ifdef XZ_PREBOOT ++STATIC int INIT __decompress(unsigned char *buf, long len, ++ long (*fill)(void*, unsigned long), ++ long (*flush)(void*, unsigned long), ++ unsigned char *out_buf, long olen, ++ long *pos, ++ void (*error)(char *x)) ++{ ++ return unxz(buf, len, fill, flush, out_buf, pos, error); ++} ++#endif +diff --git a/mm/vmscan.c b/mm/vmscan.c +index 8286938c70de..26c86e2fb5af 100644 +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1190,7 +1190,7 @@ cull_mlocked: + if (PageSwapCache(page)) + try_to_free_swap(page); + unlock_page(page); +- putback_lru_page(page); ++ list_add(&page->lru, &ret_pages); + continue; + + activate_locked: +diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c +index b8233505bf9f..8f1df6793650 100644 +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -311,9 +311,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) + if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) + return TX_CONTINUE; + +- if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT) +- return TX_CONTINUE; +- + if (tx->flags & IEEE80211_TX_PS_BUFFERED) + return TX_CONTINUE; + +diff --git a/net/nfc/nci/hci.c b/net/nfc/nci/hci.c +index af002df640c7..609f92283d1b 100644 +--- a/net/nfc/nci/hci.c ++++ b/net/nfc/nci/hci.c +@@ -233,7 +233,7 @@ int nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd, + r = nci_request(ndev, nci_hci_send_data_req, (unsigned long)&data, + msecs_to_jiffies(NCI_DATA_TIMEOUT)); + +- if (r == NCI_STATUS_OK) ++ if (r == NCI_STATUS_OK && skb) + *skb = conn_info->rx_skb; + + return r; +diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c +index f85f37ed19b2..73d1ca7c546c 100644 +--- a/net/nfc/netlink.c ++++ b/net/nfc/netlink.c +@@ -1518,12 +1518,13 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb, + if (!dev || !dev->vendor_cmds || !dev->n_vendor_cmds) + return -ENODEV; + +- data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]); +- if (data) { ++ if (info->attrs[NFC_ATTR_VENDOR_DATA]) { ++ data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]); + data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]); + if (data_len == 0) + return -EINVAL; + } else { ++ data = NULL; + data_len = 0; + } + +diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c +index ab5dd621ae0c..2e98f4a243e5 100644 +--- a/net/sunrpc/xprt.c ++++ b/net/sunrpc/xprt.c +@@ -614,6 +614,7 @@ static void xprt_autoclose(struct work_struct *work) + clear_bit(XPRT_CLOSE_WAIT, &xprt->state); + xprt->ops->close(xprt); + xprt_release_write(xprt, NULL); ++ wake_up_bit(&xprt->state, XPRT_LOCKED); + } + + /** +@@ -723,6 +724,7 @@ void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie) + xprt->ops->release_xprt(xprt, NULL); + out: + spin_unlock_bh(&xprt->transport_lock); ++ wake_up_bit(&xprt->state, XPRT_LOCKED); + } + + /** +@@ -1394,6 +1396,10 @@ out: + static void xprt_destroy(struct rpc_xprt *xprt) + { + dprintk("RPC: destroying transport %p\n", xprt); ++ ++ /* Exclude transport connect/disconnect handlers */ ++ wait_on_bit_lock(&xprt->state, XPRT_LOCKED, TASK_UNINTERRUPTIBLE); ++ + del_timer_sync(&xprt->timer); + + rpc_xprt_debugfs_unregister(xprt); +diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c +index 6b36279e4288..48f6de912f78 100644 +--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c +@@ -91,7 +91,7 @@ struct svc_xprt_class svc_rdma_class = { + .xcl_name = "rdma", + .xcl_owner = THIS_MODULE, + .xcl_ops = &svc_rdma_ops, +- .xcl_max_payload = RPCRDMA_MAXPAYLOAD, ++ .xcl_max_payload = RPCSVC_MAXPAYLOAD_RDMA, + .xcl_ident = XPRT_TRANSPORT_RDMA, + }; + +diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h +index f49dd8b38122..e718d0959af3 100644 +--- a/net/sunrpc/xprtrdma/xprt_rdma.h ++++ b/net/sunrpc/xprtrdma/xprt_rdma.h +@@ -51,7 +51,6 @@ + #include /* rpc_xprt */ + #include /* RPC/RDMA protocol */ + #include /* xprt parameters */ +-#include /* RPCSVC_MAXPAYLOAD */ + + #define RDMA_RESOLVE_TIMEOUT (5000) /* 5 seconds */ + #define RDMA_CONNECT_RETRY_MAX (2) /* retries if no listener backlog */ +diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c +index 0030376327b7..8a39b1e48bc4 100644 +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -829,6 +829,7 @@ static void xs_reset_transport(struct sock_xprt *transport) + sk->sk_user_data = NULL; + + xs_restore_old_callbacks(transport, sk); ++ xprt_clear_connected(xprt); + write_unlock_bh(&sk->sk_callback_lock); + xs_sock_reset_connection_flags(xprt); + +@@ -1432,6 +1433,7 @@ out: + static void xs_tcp_state_change(struct sock *sk) + { + struct rpc_xprt *xprt; ++ struct sock_xprt *transport; + + read_lock_bh(&sk->sk_callback_lock); + if (!(xprt = xprt_from_sock(sk))) +@@ -1443,13 +1445,12 @@ static void xs_tcp_state_change(struct sock *sk) + sock_flag(sk, SOCK_ZAPPED), + sk->sk_shutdown); + ++ transport = container_of(xprt, struct sock_xprt, xprt); + trace_rpc_socket_state_change(xprt, sk->sk_socket); + switch (sk->sk_state) { + case TCP_ESTABLISHED: + spin_lock(&xprt->transport_lock); + if (!xprt_test_and_set_connected(xprt)) { +- struct sock_xprt *transport = container_of(xprt, +- struct sock_xprt, xprt); + + /* Reset TCP record info */ + transport->tcp_offset = 0; +@@ -1458,6 +1459,8 @@ static void xs_tcp_state_change(struct sock *sk) + transport->tcp_flags = + TCP_RCV_COPY_FRAGHDR | TCP_RCV_COPY_XID; + xprt->connect_cookie++; ++ clear_bit(XPRT_SOCK_CONNECTING, &transport->sock_state); ++ xprt_clear_connecting(xprt); + + xprt_wake_pending_tasks(xprt, -EAGAIN); + } +@@ -1493,6 +1496,9 @@ static void xs_tcp_state_change(struct sock *sk) + smp_mb__after_atomic(); + break; + case TCP_CLOSE: ++ if (test_and_clear_bit(XPRT_SOCK_CONNECTING, ++ &transport->sock_state)) ++ xprt_clear_connecting(xprt); + xs_sock_mark_closed(xprt); + } + out: +@@ -2176,6 +2182,7 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock) + /* Tell the socket layer to start connecting... */ + xprt->stat.connect_count++; + xprt->stat.connect_start = jiffies; ++ set_bit(XPRT_SOCK_CONNECTING, &transport->sock_state); + ret = kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK); + switch (ret) { + case 0: +@@ -2237,7 +2244,6 @@ static void xs_tcp_setup_socket(struct work_struct *work) + case -EINPROGRESS: + case -EALREADY: + xprt_unlock_connect(xprt, transport); +- xprt_clear_connecting(xprt); + return; + case -EINVAL: + /* Happens, for instance, if the user specified a link +@@ -2279,13 +2285,14 @@ static void xs_connect(struct rpc_xprt *xprt, struct rpc_task *task) + + WARN_ON_ONCE(!xprt_lock_connect(xprt, task, transport)); + +- /* Start by resetting any existing state */ +- xs_reset_transport(transport); +- +- if (transport->sock != NULL && !RPC_IS_SOFTCONN(task)) { ++ if (transport->sock != NULL) { + dprintk("RPC: xs_connect delayed xprt %p for %lu " + "seconds\n", + xprt, xprt->reestablish_timeout / HZ); ++ ++ /* Start by resetting any existing state */ ++ xs_reset_transport(transport); ++ + queue_delayed_work(rpciod_workqueue, + &transport->connect_worker, + xprt->reestablish_timeout); +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index 374ea53288ca..c8f01ccc2513 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -1135,7 +1135,7 @@ static const struct hda_fixup alc880_fixups[] = { + /* override all pins as BIOS on old Amilo is broken */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { +- { 0x14, 0x0121411f }, /* HP */ ++ { 0x14, 0x0121401f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x99030130 }, /* bass speaker */ + { 0x17, 0x411111f0 }, /* N/A */ +@@ -1155,7 +1155,7 @@ static const struct hda_fixup alc880_fixups[] = { + /* almost compatible with FUJITSU, but no bass and SPDIF */ + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { +- { 0x14, 0x0121411f }, /* HP */ ++ { 0x14, 0x0121401f }, /* HP */ + { 0x15, 0x99030120 }, /* speaker */ + { 0x16, 0x411111f0 }, /* N/A */ + { 0x17, 0x411111f0 }, /* N/A */ +@@ -1364,7 +1364,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { + SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_FIXUP_W810), + SND_PCI_QUIRK(0x161f, 0x205d, "Medion Rim 2150", ALC880_FIXUP_MEDION_RIM), + SND_PCI_QUIRK(0x1631, 0xe011, "PB 13201056", ALC880_FIXUP_6ST_AUTOMUTE), +- SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_FIXUP_F1734), ++ SND_PCI_QUIRK(0x1734, 0x107c, "FSC Amilo M1437", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x1094, "FSC Amilo M1451G", ALC880_FIXUP_FUJITSU), + SND_PCI_QUIRK(0x1734, 0x10ac, "FSC AMILO Xi 1526", ALC880_FIXUP_F1734), + SND_PCI_QUIRK(0x1734, 0x10b0, "FSC Amilo Pi1556", ALC880_FIXUP_FUJITSU), +@@ -5189,8 +5189,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), +- SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC292_FIXUP_DISABLE_AAMIX), + SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC292_FIXUP_DISABLE_AAMIX), ++ SND_PCI_QUIRK(0x1028, 0x06dd, "Dell", ALC292_FIXUP_DISABLE_AAMIX), ++ SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC292_FIXUP_DISABLE_AAMIX), ++ SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC292_FIXUP_DISABLE_AAMIX), ++ SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC292_FIXUP_DISABLE_AAMIX), + SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), +@@ -6579,6 +6582,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { + SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x05fe, "Dell XPS 15", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x060a, "Dell XPS 13", ALC668_FIXUP_DELL_XPS13), ++ SND_PCI_QUIRK(0x1028, 0x060d, "Dell M3800", ALC668_FIXUP_DELL_XPS13), + SND_PCI_QUIRK(0x1028, 0x0625, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), +diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c +index 6b3acba5da7a..83d6e76435b4 100644 +--- a/sound/usb/mixer.c ++++ b/sound/usb/mixer.c +@@ -2522,7 +2522,7 @@ static int restore_mixer_value(struct usb_mixer_elem_list *list) + for (c = 0; c < MAX_CHANNELS; c++) { + if (!(cval->cmask & (1 << c))) + continue; +- if (cval->cached & (1 << c)) { ++ if (cval->cached & (1 << (c + 1))) { + err = snd_usb_set_cur_mix_value(cval, c + 1, idx, + cval->cache_val[idx]); + if (err < 0) diff --git a/patch/kernel/odroid-next/patch-4.2.2-3.patch b/patch/kernel/odroid-next/patch-4.2.2-3.patch new file mode 100644 index 000000000..018e36cc0 --- /dev/null +++ b/patch/kernel/odroid-next/patch-4.2.2-3.patch @@ -0,0 +1,1532 @@ +diff --git a/Documentation/devicetree/bindings/net/ethernet.txt b/Documentation/devicetree/bindings/net/ethernet.txt +index 41b3f3f864e8..5d88f37480b6 100644 +--- a/Documentation/devicetree/bindings/net/ethernet.txt ++++ b/Documentation/devicetree/bindings/net/ethernet.txt +@@ -25,7 +25,11 @@ The following properties are common to the Ethernet controllers: + flow control thresholds. + - tx-fifo-depth: the size of the controller's transmit fifo in bytes. This + is used for components that can have configurable fifo sizes. ++- managed: string, specifies the PHY management type. Supported values are: ++ "auto", "in-band-status". "auto" is the default, it usess MDIO for ++ management if fixed-link is not specified. + + Child nodes of the Ethernet controller are typically the individual PHY devices + connected via the MDIO bus (sometimes the MDIO bus controller is separate). + They are described in the phy.txt file in this same directory. ++For non-MDIO PHY management see fixed-link.txt. +diff --git a/Makefile b/Makefile +index 3578b4426ecf..a6edbb11a69a 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 2 +-SUBLEVEL = 2 ++SUBLEVEL = 3 + EXTRAVERSION = + NAME = Hurr durr I'ma sheep + +diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c +index 965d1afb0eaa..5cb13ca3a3ac 100644 +--- a/drivers/block/zram/zcomp.c ++++ b/drivers/block/zram/zcomp.c +@@ -330,12 +330,14 @@ void zcomp_destroy(struct zcomp *comp) + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in +- * case of allocation error. ++ * case of allocation error, or any other error potentially ++ * returned by functions zcomp_strm_{multi,single}_create. + */ + struct zcomp *zcomp_create(const char *compress, int max_strm) + { + struct zcomp *comp; + struct zcomp_backend *backend; ++ int error; + + backend = find_backend(compress); + if (!backend) +@@ -347,12 +349,12 @@ struct zcomp *zcomp_create(const char *compress, int max_strm) + + comp->backend = backend; + if (max_strm > 1) +- zcomp_strm_multi_create(comp, max_strm); ++ error = zcomp_strm_multi_create(comp, max_strm); + else +- zcomp_strm_single_create(comp); +- if (!comp->stream) { ++ error = zcomp_strm_single_create(comp); ++ if (error) { + kfree(comp); +- return ERR_PTR(-ENOMEM); ++ return ERR_PTR(error); + } + return comp; + } +diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c +index 079897b3a955..9d56515f4c4d 100644 +--- a/drivers/net/dsa/bcm_sf2.c ++++ b/drivers/net/dsa/bcm_sf2.c +@@ -418,7 +418,7 @@ static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) + core_writel(priv, port, CORE_FAST_AGE_PORT); + + reg = core_readl(priv, CORE_FAST_AGE_CTRL); +- reg |= EN_AGE_PORT | FAST_AGE_STR_DONE; ++ reg |= EN_AGE_PORT | EN_AGE_DYNAMIC | FAST_AGE_STR_DONE; + core_writel(priv, reg, CORE_FAST_AGE_CTRL); + + do { +@@ -432,6 +432,8 @@ static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) + if (!timeout) + return -ETIMEDOUT; + ++ core_writel(priv, 0, CORE_FAST_AGE_CTRL); ++ + return 0; + } + +@@ -507,7 +509,7 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, + u32 reg; + + reg = core_readl(priv, CORE_G_PCTL_PORT(port)); +- cur_hw_state = reg >> G_MISTP_STATE_SHIFT; ++ cur_hw_state = reg & (G_MISTP_STATE_MASK << G_MISTP_STATE_SHIFT); + + switch (state) { + case BR_STATE_DISABLED: +@@ -531,10 +533,12 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, + } + + /* Fast-age ARL entries if we are moving a port from Learning or +- * Forwarding state to Disabled, Blocking or Listening state ++ * Forwarding (cur_hw_state) state to Disabled, Blocking or Listening ++ * state (hw_state) + */ + if (cur_hw_state != hw_state) { +- if (cur_hw_state & 4 && !(hw_state & 4)) { ++ if (cur_hw_state >= G_MISTP_LEARN_STATE && ++ hw_state <= G_MISTP_LISTEN_STATE) { + ret = bcm_sf2_sw_fast_age_port(ds, port); + if (ret) { + pr_err("%s: fast-ageing failed\n", __func__); +@@ -901,15 +905,11 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, + struct fixed_phy_status *status) + { + struct bcm_sf2_priv *priv = ds_to_priv(ds); +- u32 duplex, pause, speed; ++ u32 duplex, pause; + u32 reg; + + duplex = core_readl(priv, CORE_DUPSTS); + pause = core_readl(priv, CORE_PAUSESTS); +- speed = core_readl(priv, CORE_SPDSTS); +- +- speed >>= (port * SPDSTS_SHIFT); +- speed &= SPDSTS_MASK; + + status->link = 0; + +@@ -944,18 +944,6 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, + reg &= ~LINK_STS; + core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); + +- switch (speed) { +- case SPDSTS_10: +- status->speed = SPEED_10; +- break; +- case SPDSTS_100: +- status->speed = SPEED_100; +- break; +- case SPDSTS_1000: +- status->speed = SPEED_1000; +- break; +- } +- + if ((pause & (1 << port)) && + (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) { + status->asym_pause = 1; +diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h +index 22e2ebf31333..789d7b7737da 100644 +--- a/drivers/net/dsa/bcm_sf2.h ++++ b/drivers/net/dsa/bcm_sf2.h +@@ -112,8 +112,8 @@ static inline u64 name##_readq(struct bcm_sf2_priv *priv, u32 off) \ + spin_unlock(&priv->indir_lock); \ + return (u64)indir << 32 | dir; \ + } \ +-static inline void name##_writeq(struct bcm_sf2_priv *priv, u32 off, \ +- u64 val) \ ++static inline void name##_writeq(struct bcm_sf2_priv *priv, u64 val, \ ++ u32 off) \ + { \ + spin_lock(&priv->indir_lock); \ + reg_writel(priv, upper_32_bits(val), REG_DIR_DATA_WRITE); \ +diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c +index 561342466076..26ec2fbfaa89 100644 +--- a/drivers/net/dsa/mv88e6xxx.c ++++ b/drivers/net/dsa/mv88e6xxx.c +@@ -1387,6 +1387,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) + reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL); + if (dsa_is_cpu_port(ds, port) || + ds->dsa_port_mask & (1 << port)) { ++ reg &= ~PORT_PCS_CTRL_UNFORCED; + reg |= PORT_PCS_CTRL_FORCE_LINK | + PORT_PCS_CTRL_LINK_UP | + PORT_PCS_CTRL_DUPLEX_FULL | +diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c +index da48e66377b5..8207877d6237 100644 +--- a/drivers/net/ethernet/altera/altera_tse_main.c ++++ b/drivers/net/ethernet/altera/altera_tse_main.c +@@ -511,8 +511,7 @@ static int tse_poll(struct napi_struct *napi, int budget) + + if (rxcomplete < budget) { + +- napi_gro_flush(napi, false); +- __napi_complete(napi); ++ napi_complete(napi); + + netdev_dbg(priv->dev, + "NAPI Complete, did %d packets with budget %d\n", +diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c +index b349e6f36ea7..de63266de16b 100644 +--- a/drivers/net/ethernet/freescale/fec_main.c ++++ b/drivers/net/ethernet/freescale/fec_main.c +@@ -1402,6 +1402,7 @@ fec_enet_rx_queue(struct net_device *ndev, int budget, u16 queue_id) + if ((status & BD_ENET_RX_LAST) == 0) + netdev_err(ndev, "rcv is not +last\n"); + ++ writel(FEC_ENET_RXF, fep->hwp + FEC_IEVENT); + + /* Check for errors. */ + if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO | +diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c +index 62e48bc0cb23..09ec32e33076 100644 +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -1479,6 +1479,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, + struct mvneta_rx_desc *rx_desc = mvneta_rxq_next_desc_get(rxq); + struct sk_buff *skb; + unsigned char *data; ++ dma_addr_t phys_addr; + u32 rx_status; + int rx_bytes, err; + +@@ -1486,6 +1487,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, + rx_status = rx_desc->status; + rx_bytes = rx_desc->data_size - (ETH_FCS_LEN + MVNETA_MH_SIZE); + data = (unsigned char *)rx_desc->buf_cookie; ++ phys_addr = rx_desc->buf_phys_addr; + + if (!mvneta_rxq_desc_is_first_last(rx_status) || + (rx_status & MVNETA_RXD_ERR_SUMMARY)) { +@@ -1534,7 +1536,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo, + if (!skb) + goto err_drop_frame; + +- dma_unmap_single(dev->dev.parent, rx_desc->buf_phys_addr, ++ dma_unmap_single(dev->dev.parent, phys_addr, + MVNETA_RX_BUF_SIZE(pp->pkt_size), DMA_FROM_DEVICE); + + rcvd_pkts++; +@@ -3027,8 +3029,8 @@ static int mvneta_probe(struct platform_device *pdev) + const char *dt_mac_addr; + char hw_mac_addr[ETH_ALEN]; + const char *mac_from; ++ const char *managed; + int phy_mode; +- int fixed_phy = 0; + int err; + + /* Our multiqueue support is not complete, so for now, only +@@ -3062,7 +3064,6 @@ static int mvneta_probe(struct platform_device *pdev) + dev_err(&pdev->dev, "cannot register fixed PHY\n"); + goto err_free_irq; + } +- fixed_phy = 1; + + /* In the case of a fixed PHY, the DT node associated + * to the PHY is the Ethernet MAC DT node. +@@ -3086,8 +3087,10 @@ static int mvneta_probe(struct platform_device *pdev) + pp = netdev_priv(dev); + pp->phy_node = phy_node; + pp->phy_interface = phy_mode; +- pp->use_inband_status = (phy_mode == PHY_INTERFACE_MODE_SGMII) && +- fixed_phy; ++ ++ err = of_property_read_string(dn, "managed", &managed); ++ pp->use_inband_status = (err == 0 && ++ strcmp(managed, "in-band-status") == 0); + + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { +diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c +index 9c145dddd717..4f95fa7b594d 100644 +--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c ++++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c +@@ -1250,8 +1250,6 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) + rss_context->hash_fn = MLX4_RSS_HASH_TOP; + memcpy(rss_context->rss_key, priv->rss_key, + MLX4_EN_RSS_KEY_SIZE); +- netdev_rss_key_fill(rss_context->rss_key, +- MLX4_EN_RSS_KEY_SIZE); + } else { + en_err(priv, "Unknown RSS hash function requested\n"); + err = -EINVAL; +diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c +index 29c2a017a450..a408977a531a 100644 +--- a/drivers/net/ethernet/mellanox/mlx4/main.c ++++ b/drivers/net/ethernet/mellanox/mlx4/main.c +@@ -2654,9 +2654,14 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) + + if (msi_x) { + int nreq = dev->caps.num_ports * num_online_cpus() + 1; ++ bool shared_ports = false; + + nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs, + nreq); ++ if (nreq > MAX_MSIX) { ++ nreq = MAX_MSIX; ++ shared_ports = true; ++ } + + entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL); + if (!entries) +@@ -2679,6 +2684,9 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) + bitmap_zero(priv->eq_table.eq[MLX4_EQ_ASYNC].actv_ports.ports, + dev->caps.num_ports); + ++ if (MLX4_IS_LEGACY_EQ_MODE(dev->caps)) ++ shared_ports = true; ++ + for (i = 0; i < dev->caps.num_comp_vectors + 1; i++) { + if (i == MLX4_EQ_ASYNC) + continue; +@@ -2686,7 +2694,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) + priv->eq_table.eq[i].irq = + entries[i + 1 - !!(i > MLX4_EQ_ASYNC)].vector; + +- if (MLX4_IS_LEGACY_EQ_MODE(dev->caps)) { ++ if (shared_ports) { + bitmap_fill(priv->eq_table.eq[i].actv_ports.ports, + dev->caps.num_ports); + /* We don't set affinity hint when there +diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c +index edd77342773a..248478c6f6e4 100644 +--- a/drivers/net/macvtap.c ++++ b/drivers/net/macvtap.c +@@ -1111,10 +1111,10 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, + return 0; + + case TUNSETSNDBUF: +- if (get_user(u, up)) ++ if (get_user(s, sp)) + return -EFAULT; + +- q->sk.sk_sndbuf = u; ++ q->sk.sk_sndbuf = s; + return 0; + + case TUNGETVNETHDRSZ: +diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c +index d7a65247f952..99d9bc19c94a 100644 +--- a/drivers/net/phy/fixed_phy.c ++++ b/drivers/net/phy/fixed_phy.c +@@ -52,6 +52,10 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) + u16 lpagb = 0; + u16 lpa = 0; + ++ if (!fp->status.link) ++ goto done; ++ bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; ++ + if (fp->status.duplex) { + bmcr |= BMCR_FULLDPLX; + +@@ -96,15 +100,13 @@ static int fixed_phy_update_regs(struct fixed_phy *fp) + } + } + +- if (fp->status.link) +- bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; +- + if (fp->status.pause) + lpa |= LPA_PAUSE_CAP; + + if (fp->status.asym_pause) + lpa |= LPA_PAUSE_ASYM; + ++done: + fp->regs[MII_PHYSID1] = 0; + fp->regs[MII_PHYSID2] = 0; + +diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c +index 46a14cbb0215..02a4615b65f8 100644 +--- a/drivers/net/phy/mdio_bus.c ++++ b/drivers/net/phy/mdio_bus.c +@@ -303,12 +303,12 @@ void mdiobus_unregister(struct mii_bus *bus) + BUG_ON(bus->state != MDIOBUS_REGISTERED); + bus->state = MDIOBUS_UNREGISTERED; + +- device_del(&bus->dev); + for (i = 0; i < PHY_MAX_ADDR; i++) { + if (bus->phy_map[i]) + device_unregister(&bus->phy_map[i]->dev); + bus->phy_map[i] = NULL; + } ++ device_del(&bus->dev); + } + EXPORT_SYMBOL(mdiobus_unregister); + +diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c +index fa8f5046afe9..487be20b6b12 100644 +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -2742,6 +2742,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, + */ + dev_net_set(dev, net); + ++ rtnl_lock(); + mutex_lock(&pn->all_ppp_mutex); + + if (unit < 0) { +@@ -2772,7 +2773,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, + ppp->file.index = unit; + sprintf(dev->name, "ppp%d", unit); + +- ret = register_netdev(dev); ++ ret = register_netdevice(dev); + if (ret != 0) { + unit_put(&pn->units_idr, unit); + netdev_err(ppp->dev, "PPP: couldn't register device %s (%d)\n", +@@ -2784,6 +2785,7 @@ static struct ppp *ppp_create_interface(struct net *net, int unit, + + atomic_inc(&ppp_unit_count); + mutex_unlock(&pn->all_ppp_mutex); ++ rtnl_unlock(); + + *retp = 0; + return ppp; +diff --git a/drivers/of/of_mdio.c b/drivers/of/of_mdio.c +index fdc60db60829..7c8c23cc6896 100644 +--- a/drivers/of/of_mdio.c ++++ b/drivers/of/of_mdio.c +@@ -266,7 +266,8 @@ EXPORT_SYMBOL(of_phy_attach); + bool of_phy_is_fixed_link(struct device_node *np) + { + struct device_node *dn; +- int len; ++ int len, err; ++ const char *managed; + + /* New binding */ + dn = of_get_child_by_name(np, "fixed-link"); +@@ -275,6 +276,10 @@ bool of_phy_is_fixed_link(struct device_node *np) + return true; + } + ++ err = of_property_read_string(np, "managed", &managed); ++ if (err == 0 && strcmp(managed, "auto") != 0) ++ return true; ++ + /* Old binding */ + if (of_get_property(np, "fixed-link", &len) && + len == (5 * sizeof(__be32))) +@@ -289,8 +294,18 @@ int of_phy_register_fixed_link(struct device_node *np) + struct fixed_phy_status status = {}; + struct device_node *fixed_link_node; + const __be32 *fixed_link_prop; +- int len; ++ int len, err; + struct phy_device *phy; ++ const char *managed; ++ ++ err = of_property_read_string(np, "managed", &managed); ++ if (err == 0) { ++ if (strcmp(managed, "in-band-status") == 0) { ++ /* status is zeroed, namely its .link member */ ++ phy = fixed_phy_register(PHY_POLL, &status, np); ++ return IS_ERR(phy) ? PTR_ERR(phy) : 0; ++ } ++ } + + /* New binding */ + fixed_link_node = of_get_child_by_name(np, "fixed-link"); +diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c +index 06697315a088..fb4dd7b3ee71 100644 +--- a/drivers/platform/x86/hp-wmi.c ++++ b/drivers/platform/x86/hp-wmi.c +@@ -54,8 +54,9 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); + #define HPWMI_HARDWARE_QUERY 0x4 + #define HPWMI_WIRELESS_QUERY 0x5 + #define HPWMI_BIOS_QUERY 0x9 ++#define HPWMI_FEATURE_QUERY 0xb + #define HPWMI_HOTKEY_QUERY 0xc +-#define HPWMI_FEATURE_QUERY 0xd ++#define HPWMI_FEATURE2_QUERY 0xd + #define HPWMI_WIRELESS2_QUERY 0x1b + #define HPWMI_POSTCODEERROR_QUERY 0x2a + +@@ -295,25 +296,33 @@ static int hp_wmi_tablet_state(void) + return (state & 0x4) ? 1 : 0; + } + +-static int __init hp_wmi_bios_2009_later(void) ++static int __init hp_wmi_bios_2008_later(void) + { + int state = 0; + int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state, + sizeof(state), sizeof(state)); +- if (ret) +- return ret; ++ if (!ret) ++ return 1; + +- return (state & 0x10) ? 1 : 0; ++ return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; + } + +-static int hp_wmi_enable_hotkeys(void) ++static int __init hp_wmi_bios_2009_later(void) + { +- int ret; +- int query = 0x6e; ++ int state = 0; ++ int ret = hp_wmi_perform_query(HPWMI_FEATURE2_QUERY, 0, &state, ++ sizeof(state), sizeof(state)); ++ if (!ret) ++ return 1; + +- ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), +- 0); ++ return (ret == HPWMI_RET_UNKNOWN_CMDTYPE) ? 0 : -ENXIO; ++} + ++static int __init hp_wmi_enable_hotkeys(void) ++{ ++ int value = 0x6e; ++ int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &value, ++ sizeof(value), 0); + if (ret) + return -EINVAL; + return 0; +@@ -663,7 +672,7 @@ static int __init hp_wmi_input_setup(void) + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); + +- if (hp_wmi_bios_2009_later() == 4) ++ if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later()) + hp_wmi_enable_hotkeys(); + + status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL); +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index 1285eaf5dc22..03cdb9e18d57 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -991,7 +991,7 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br, + + ih = igmpv3_report_hdr(skb); + num = ntohs(ih->ngrec); +- len = sizeof(*ih); ++ len = skb_transport_offset(skb) + sizeof(*ih); + + for (i = 0; i < num; i++) { + len += sizeof(*grec); +@@ -1052,7 +1052,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, + + icmp6h = icmp6_hdr(skb); + num = ntohs(icmp6h->icmp6_dataun.un_data16[1]); +- len = sizeof(*icmp6h); ++ len = skb_transport_offset(skb) + sizeof(*icmp6h); + + for (i = 0; i < num; i++) { + __be16 *nsrcs, _nsrcs; +diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c +index 9a12668f7d62..0ad144fb0c79 100644 +--- a/net/core/fib_rules.c ++++ b/net/core/fib_rules.c +@@ -615,15 +615,17 @@ static int dump_rules(struct sk_buff *skb, struct netlink_callback *cb, + { + int idx = 0; + struct fib_rule *rule; ++ int err = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(rule, &ops->rules_list, list) { + if (idx < cb->args[1]) + goto skip; + +- if (fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).portid, +- cb->nlh->nlmsg_seq, RTM_NEWRULE, +- NLM_F_MULTI, ops) < 0) ++ err = fib_nl_fill_rule(skb, rule, NETLINK_CB(cb->skb).portid, ++ cb->nlh->nlmsg_seq, RTM_NEWRULE, ++ NLM_F_MULTI, ops); ++ if (err) + break; + skip: + idx++; +@@ -632,7 +634,7 @@ skip: + cb->args[1] = idx; + rules_ops_put(ops); + +- return skb->len; ++ return err; + } + + static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) +@@ -648,7 +650,9 @@ static int fib_nl_dumprule(struct sk_buff *skb, struct netlink_callback *cb) + if (ops == NULL) + return -EAFNOSUPPORT; + +- return dump_rules(skb, cb, ops); ++ dump_rules(skb, cb, ops); ++ ++ return skb->len; + } + + rcu_read_lock(); +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index dc004b1e1f85..0861018be708 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -3021,6 +3021,7 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) + u32 portid = NETLINK_CB(cb->skb).portid; + u32 seq = cb->nlh->nlmsg_seq; + u32 filter_mask = 0; ++ int err; + + if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) { + struct nlattr *extfilt; +@@ -3041,20 +3042,25 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + + if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { +- if (idx >= cb->args[0] && +- br_dev->netdev_ops->ndo_bridge_getlink( +- skb, portid, seq, dev, filter_mask, +- NLM_F_MULTI) < 0) +- break; ++ if (idx >= cb->args[0]) { ++ err = br_dev->netdev_ops->ndo_bridge_getlink( ++ skb, portid, seq, dev, ++ filter_mask, NLM_F_MULTI); ++ if (err < 0 && err != -EOPNOTSUPP) ++ break; ++ } + idx++; + } + + if (ops->ndo_bridge_getlink) { +- if (idx >= cb->args[0] && +- ops->ndo_bridge_getlink(skb, portid, seq, dev, +- filter_mask, +- NLM_F_MULTI) < 0) +- break; ++ if (idx >= cb->args[0]) { ++ err = ops->ndo_bridge_getlink(skb, portid, ++ seq, dev, ++ filter_mask, ++ NLM_F_MULTI); ++ if (err < 0 && err != -EOPNOTSUPP) ++ break; ++ } + idx++; + } + } +diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c +index d79866c5f8bc..817622f3dbb7 100644 +--- a/net/core/sock_diag.c ++++ b/net/core/sock_diag.c +@@ -90,6 +90,9 @@ int sock_diag_put_filterinfo(bool may_report_filterinfo, struct sock *sk, + goto out; + + fprog = filter->prog->orig_prog; ++ if (!fprog) ++ goto out; ++ + flen = bpf_classic_proglen(fprog); + + attr = nla_reserve(skb, attrtype, flen); +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index b1c218df2c85..b7dedd9d36d8 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -2898,6 +2898,7 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority) + skb_reserve(skb, MAX_TCP_HEADER); + tcp_init_nondata_skb(skb, tcp_acceptable_seq(sk), + TCPHDR_ACK | TCPHDR_RST); ++ skb_mstamp_get(&skb->skb_mstamp); + /* Send it off. */ + if (tcp_transmit_skb(sk, skb, 0, priority)) + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED); +diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c +index 447a7fbd1bb6..f5e2ba1c18bf 100644 +--- a/net/ipv6/exthdrs_offload.c ++++ b/net/ipv6/exthdrs_offload.c +@@ -36,6 +36,6 @@ out: + return ret; + + out_rt: +- inet_del_offload(&rthdr_offload, IPPROTO_ROUTING); ++ inet6_del_offload(&rthdr_offload, IPPROTO_ROUTING); + goto out; + } +diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c +index 74ceb73c1c9a..5f36266b1f5e 100644 +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -550,7 +550,7 @@ static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v) + + if (it->cache == &mrt->mfc6_unres_queue) + spin_unlock_bh(&mfc_unres_lock); +- else if (it->cache == mrt->mfc6_cache_array) ++ else if (it->cache == &mrt->mfc6_cache_array[it->ct]) + read_unlock(&mrt_lock); + } + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index d15586490cec..00b64d402a57 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -1727,7 +1727,7 @@ static int ip6_convert_metrics(struct mx6_config *mxc, + return -EINVAL; + } + +-int ip6_route_add(struct fib6_config *cfg) ++int ip6_route_info_create(struct fib6_config *cfg, struct rt6_info **rt_ret) + { + int err; + struct net *net = cfg->fc_nlinfo.nl_net; +@@ -1735,7 +1735,6 @@ int ip6_route_add(struct fib6_config *cfg) + struct net_device *dev = NULL; + struct inet6_dev *idev = NULL; + struct fib6_table *table; +- struct mx6_config mxc = { .mx = NULL, }; + int addr_type; + + if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) +@@ -1941,6 +1940,32 @@ install_route: + + cfg->fc_nlinfo.nl_net = dev_net(dev); + ++ *rt_ret = rt; ++ ++ return 0; ++out: ++ if (dev) ++ dev_put(dev); ++ if (idev) ++ in6_dev_put(idev); ++ if (rt) ++ dst_free(&rt->dst); ++ ++ *rt_ret = NULL; ++ ++ return err; ++} ++ ++int ip6_route_add(struct fib6_config *cfg) ++{ ++ struct mx6_config mxc = { .mx = NULL, }; ++ struct rt6_info *rt = NULL; ++ int err; ++ ++ err = ip6_route_info_create(cfg, &rt); ++ if (err) ++ goto out; ++ + err = ip6_convert_metrics(&mxc, cfg); + if (err) + goto out; +@@ -1948,14 +1973,12 @@ install_route: + err = __ip6_ins_rt(rt, &cfg->fc_nlinfo, &mxc); + + kfree(mxc.mx); ++ + return err; + out: +- if (dev) +- dev_put(dev); +- if (idev) +- in6_dev_put(idev); + if (rt) + dst_free(&rt->dst); ++ + return err; + } + +@@ -2727,19 +2750,78 @@ errout: + return err; + } + +-static int ip6_route_multipath(struct fib6_config *cfg, int add) ++struct rt6_nh { ++ struct rt6_info *rt6_info; ++ struct fib6_config r_cfg; ++ struct mx6_config mxc; ++ struct list_head next; ++}; ++ ++static void ip6_print_replace_route_err(struct list_head *rt6_nh_list) ++{ ++ struct rt6_nh *nh; ++ ++ list_for_each_entry(nh, rt6_nh_list, next) { ++ pr_warn("IPV6: multipath route replace failed (check consistency of installed routes): %pI6 nexthop %pI6 ifi %d\n", ++ &nh->r_cfg.fc_dst, &nh->r_cfg.fc_gateway, ++ nh->r_cfg.fc_ifindex); ++ } ++} ++ ++static int ip6_route_info_append(struct list_head *rt6_nh_list, ++ struct rt6_info *rt, struct fib6_config *r_cfg) ++{ ++ struct rt6_nh *nh; ++ struct rt6_info *rtnh; ++ int err = -EEXIST; ++ ++ list_for_each_entry(nh, rt6_nh_list, next) { ++ /* check if rt6_info already exists */ ++ rtnh = nh->rt6_info; ++ ++ if (rtnh->dst.dev == rt->dst.dev && ++ rtnh->rt6i_idev == rt->rt6i_idev && ++ ipv6_addr_equal(&rtnh->rt6i_gateway, ++ &rt->rt6i_gateway)) ++ return err; ++ } ++ ++ nh = kzalloc(sizeof(*nh), GFP_KERNEL); ++ if (!nh) ++ return -ENOMEM; ++ nh->rt6_info = rt; ++ err = ip6_convert_metrics(&nh->mxc, r_cfg); ++ if (err) { ++ kfree(nh); ++ return err; ++ } ++ memcpy(&nh->r_cfg, r_cfg, sizeof(*r_cfg)); ++ list_add_tail(&nh->next, rt6_nh_list); ++ ++ return 0; ++} ++ ++static int ip6_route_multipath_add(struct fib6_config *cfg) + { + struct fib6_config r_cfg; + struct rtnexthop *rtnh; ++ struct rt6_info *rt; ++ struct rt6_nh *err_nh; ++ struct rt6_nh *nh, *nh_safe; + int remaining; + int attrlen; +- int err = 0, last_err = 0; ++ int err = 1; ++ int nhn = 0; ++ int replace = (cfg->fc_nlinfo.nlh && ++ (cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_REPLACE)); ++ LIST_HEAD(rt6_nh_list); + + remaining = cfg->fc_mp_len; +-beginning: + rtnh = (struct rtnexthop *)cfg->fc_mp; + +- /* Parse a Multipath Entry */ ++ /* Parse a Multipath Entry and build a list (rt6_nh_list) of ++ * rt6_info structs per nexthop ++ */ + while (rtnh_ok(rtnh, remaining)) { + memcpy(&r_cfg, cfg, sizeof(*cfg)); + if (rtnh->rtnh_ifindex) +@@ -2755,22 +2837,32 @@ beginning: + r_cfg.fc_flags |= RTF_GATEWAY; + } + } +- err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg); ++ ++ err = ip6_route_info_create(&r_cfg, &rt); ++ if (err) ++ goto cleanup; ++ ++ err = ip6_route_info_append(&rt6_nh_list, rt, &r_cfg); + if (err) { +- last_err = err; +- /* If we are trying to remove a route, do not stop the +- * loop when ip6_route_del() fails (because next hop is +- * already gone), we should try to remove all next hops. +- */ +- if (add) { +- /* If add fails, we should try to delete all +- * next hops that have been already added. +- */ +- add = 0; +- remaining = cfg->fc_mp_len - remaining; +- goto beginning; +- } ++ dst_free(&rt->dst); ++ goto cleanup; ++ } ++ ++ rtnh = rtnh_next(rtnh, &remaining); ++ } ++ ++ err_nh = NULL; ++ list_for_each_entry(nh, &rt6_nh_list, next) { ++ err = __ip6_ins_rt(nh->rt6_info, &cfg->fc_nlinfo, &nh->mxc); ++ /* nh->rt6_info is used or freed at this point, reset to NULL*/ ++ nh->rt6_info = NULL; ++ if (err) { ++ if (replace && nhn) ++ ip6_print_replace_route_err(&rt6_nh_list); ++ err_nh = nh; ++ goto add_errout; + } ++ + /* Because each route is added like a single route we remove + * these flags after the first nexthop: if there is a collision, + * we have already failed to add the first nexthop: +@@ -2780,6 +2872,63 @@ beginning: + */ + cfg->fc_nlinfo.nlh->nlmsg_flags &= ~(NLM_F_EXCL | + NLM_F_REPLACE); ++ nhn++; ++ } ++ ++ goto cleanup; ++ ++add_errout: ++ /* Delete routes that were already added */ ++ list_for_each_entry(nh, &rt6_nh_list, next) { ++ if (err_nh == nh) ++ break; ++ ip6_route_del(&nh->r_cfg); ++ } ++ ++cleanup: ++ list_for_each_entry_safe(nh, nh_safe, &rt6_nh_list, next) { ++ if (nh->rt6_info) ++ dst_free(&nh->rt6_info->dst); ++ if (nh->mxc.mx) ++ kfree(nh->mxc.mx); ++ list_del(&nh->next); ++ kfree(nh); ++ } ++ ++ return err; ++} ++ ++static int ip6_route_multipath_del(struct fib6_config *cfg) ++{ ++ struct fib6_config r_cfg; ++ struct rtnexthop *rtnh; ++ int remaining; ++ int attrlen; ++ int err = 1, last_err = 0; ++ ++ remaining = cfg->fc_mp_len; ++ rtnh = (struct rtnexthop *)cfg->fc_mp; ++ ++ /* Parse a Multipath Entry */ ++ while (rtnh_ok(rtnh, remaining)) { ++ memcpy(&r_cfg, cfg, sizeof(*cfg)); ++ if (rtnh->rtnh_ifindex) ++ r_cfg.fc_ifindex = rtnh->rtnh_ifindex; ++ ++ attrlen = rtnh_attrlen(rtnh); ++ if (attrlen > 0) { ++ struct nlattr *nla, *attrs = rtnh_attrs(rtnh); ++ ++ nla = nla_find(attrs, attrlen, RTA_GATEWAY); ++ if (nla) { ++ nla_memcpy(&r_cfg.fc_gateway, nla, 16); ++ r_cfg.fc_flags |= RTF_GATEWAY; ++ } ++ } ++ err = ip6_route_del(&r_cfg); ++ if (err) ++ last_err = err; ++ + rtnh = rtnh_next(rtnh, &remaining); + } + +@@ -2796,7 +2945,7 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh) + return err; + + if (cfg.fc_mp) +- return ip6_route_multipath(&cfg, 0); ++ return ip6_route_multipath_del(&cfg); + else + return ip6_route_del(&cfg); + } +@@ -2811,7 +2960,7 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh) + return err; + + if (cfg.fc_mp) +- return ip6_route_multipath(&cfg, 1); ++ return ip6_route_multipath_add(&cfg); + else + return ip6_route_add(&cfg); + } +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index a774985489e2..0857f7243797 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -124,6 +124,24 @@ static inline u32 netlink_group_mask(u32 group) + return group ? 1 << (group - 1) : 0; + } + ++static struct sk_buff *netlink_to_full_skb(const struct sk_buff *skb, ++ gfp_t gfp_mask) ++{ ++ unsigned int len = skb_end_offset(skb); ++ struct sk_buff *new; ++ ++ new = alloc_skb(len, gfp_mask); ++ if (new == NULL) ++ return NULL; ++ ++ NETLINK_CB(new).portid = NETLINK_CB(skb).portid; ++ NETLINK_CB(new).dst_group = NETLINK_CB(skb).dst_group; ++ NETLINK_CB(new).creds = NETLINK_CB(skb).creds; ++ ++ memcpy(skb_put(new, len), skb->data, len); ++ return new; ++} ++ + int netlink_add_tap(struct netlink_tap *nt) + { + if (unlikely(nt->dev->type != ARPHRD_NETLINK)) +@@ -205,7 +223,11 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb, + int ret = -ENOMEM; + + dev_hold(dev); +- nskb = skb_clone(skb, GFP_ATOMIC); ++ ++ if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head)) ++ nskb = netlink_to_full_skb(skb, GFP_ATOMIC); ++ else ++ nskb = skb_clone(skb, GFP_ATOMIC); + if (nskb) { + nskb->dev = dev; + nskb->protocol = htons((u16) sk->sk_protocol); +@@ -278,11 +300,6 @@ static void netlink_rcv_wake(struct sock *sk) + } + + #ifdef CONFIG_NETLINK_MMAP +-static bool netlink_skb_is_mmaped(const struct sk_buff *skb) +-{ +- return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; +-} +- + static bool netlink_rx_is_mmaped(struct sock *sk) + { + return nlk_sk(sk)->rx_ring.pg_vec != NULL; +@@ -834,7 +851,6 @@ static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb) + } + + #else /* CONFIG_NETLINK_MMAP */ +-#define netlink_skb_is_mmaped(skb) false + #define netlink_rx_is_mmaped(sk) false + #define netlink_tx_is_mmaped(sk) false + #define netlink_mmap sock_no_mmap +@@ -1082,8 +1098,8 @@ static int netlink_insert(struct sock *sk, u32 portid) + + lock_sock(sk); + +- err = -EBUSY; +- if (nlk_sk(sk)->portid) ++ err = nlk_sk(sk)->portid == portid ? 0 : -EBUSY; ++ if (nlk_sk(sk)->bound) + goto err; + + err = -ENOMEM; +@@ -1103,10 +1119,14 @@ static int netlink_insert(struct sock *sk, u32 portid) + err = -EOVERFLOW; + if (err == -EEXIST) + err = -EADDRINUSE; +- nlk_sk(sk)->portid = 0; + sock_put(sk); ++ goto err; + } + ++ /* We need to ensure that the socket is hashed and visible. */ ++ smp_wmb(); ++ nlk_sk(sk)->bound = portid; ++ + err: + release_sock(sk); + return err; +@@ -1491,6 +1511,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, + struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; + int err; + long unsigned int groups = nladdr->nl_groups; ++ bool bound; + + if (addr_len < sizeof(struct sockaddr_nl)) + return -EINVAL; +@@ -1507,9 +1528,14 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, + return err; + } + +- if (nlk->portid) ++ bound = nlk->bound; ++ if (bound) { ++ /* Ensure nlk->portid is up-to-date. */ ++ smp_rmb(); ++ + if (nladdr->nl_pid != nlk->portid) + return -EINVAL; ++ } + + if (nlk->netlink_bind && groups) { + int group; +@@ -1525,7 +1551,10 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, + } + } + +- if (!nlk->portid) { ++ /* No need for barriers here as we return to user-space without ++ * using any of the bound attributes. ++ */ ++ if (!bound) { + err = nladdr->nl_pid ? + netlink_insert(sk, nladdr->nl_pid) : + netlink_autobind(sock); +@@ -1573,7 +1602,10 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr, + !netlink_allowed(sock, NL_CFG_F_NONROOT_SEND)) + return -EPERM; + +- if (!nlk->portid) ++ /* No need for barriers here as we return to user-space without ++ * using any of the bound attributes. ++ */ ++ if (!nlk->bound) + err = netlink_autobind(sock); + + if (err == 0) { +@@ -2391,10 +2423,13 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) + dst_group = nlk->dst_group; + } + +- if (!nlk->portid) { ++ if (!nlk->bound) { + err = netlink_autobind(sock); + if (err) + goto out; ++ } else { ++ /* Ensure nlk is hashed and visible. */ ++ smp_rmb(); + } + + /* It's a really convoluted way for userland to ask for mmaped +diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h +index 89008405d6b4..14437d9b1965 100644 +--- a/net/netlink/af_netlink.h ++++ b/net/netlink/af_netlink.h +@@ -35,6 +35,7 @@ struct netlink_sock { + unsigned long state; + size_t max_recvmsg_len; + wait_queue_head_t wait; ++ bool bound; + bool cb_running; + struct netlink_callback cb; + struct mutex *cb_mutex; +@@ -59,6 +60,15 @@ static inline struct netlink_sock *nlk_sk(struct sock *sk) + return container_of(sk, struct netlink_sock, sk); + } + ++static inline bool netlink_skb_is_mmaped(const struct sk_buff *skb) ++{ ++#ifdef CONFIG_NETLINK_MMAP ++ return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED; ++#else ++ return false; ++#endif /* CONFIG_NETLINK_MMAP */ ++} ++ + struct netlink_table { + struct rhashtable hash; + struct hlist_head mc_list; +diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c +index ff8c4a4c1609..ff332d1b94bc 100644 +--- a/net/openvswitch/datapath.c ++++ b/net/openvswitch/datapath.c +@@ -920,7 +920,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) + if (error) + goto err_kfree_flow; + +- ovs_flow_mask_key(&new_flow->key, &key, &mask); ++ ovs_flow_mask_key(&new_flow->key, &key, true, &mask); + + /* Extract flow identifier. */ + error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID], +@@ -1047,7 +1047,7 @@ static struct sw_flow_actions *get_flow_actions(const struct nlattr *a, + struct sw_flow_key masked_key; + int error; + +- ovs_flow_mask_key(&masked_key, key, mask); ++ ovs_flow_mask_key(&masked_key, key, true, mask); + error = ovs_nla_copy_actions(a, &masked_key, &acts, log); + if (error) { + OVS_NLERR(log, +diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c +index 65523948fb95..b5c3bba87fc8 100644 +--- a/net/openvswitch/flow_table.c ++++ b/net/openvswitch/flow_table.c +@@ -56,20 +56,21 @@ static u16 range_n_bytes(const struct sw_flow_key_range *range) + } + + void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, +- const struct sw_flow_mask *mask) ++ bool full, const struct sw_flow_mask *mask) + { +- const long *m = (const long *)((const u8 *)&mask->key + +- mask->range.start); +- const long *s = (const long *)((const u8 *)src + +- mask->range.start); +- long *d = (long *)((u8 *)dst + mask->range.start); ++ int start = full ? 0 : mask->range.start; ++ int len = full ? sizeof *dst : range_n_bytes(&mask->range); ++ const long *m = (const long *)((const u8 *)&mask->key + start); ++ const long *s = (const long *)((const u8 *)src + start); ++ long *d = (long *)((u8 *)dst + start); + int i; + +- /* The memory outside of the 'mask->range' are not set since +- * further operations on 'dst' only uses contents within +- * 'mask->range'. ++ /* If 'full' is true then all of 'dst' is fully initialized. Otherwise, ++ * if 'full' is false the memory outside of the 'mask->range' is left ++ * uninitialized. This can be used as an optimization when further ++ * operations on 'dst' only use contents within 'mask->range'. + */ +- for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long)) ++ for (i = 0; i < len; i += sizeof(long)) + *d++ = *s++ & *m++; + } + +@@ -473,7 +474,7 @@ static struct sw_flow *masked_flow_lookup(struct table_instance *ti, + u32 hash; + struct sw_flow_key masked_key; + +- ovs_flow_mask_key(&masked_key, unmasked, mask); ++ ovs_flow_mask_key(&masked_key, unmasked, false, mask); + hash = flow_hash(&masked_key, &mask->range); + head = find_bucket(ti, hash); + hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) { +diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h +index 616eda10d955..2dd9900f533d 100644 +--- a/net/openvswitch/flow_table.h ++++ b/net/openvswitch/flow_table.h +@@ -86,5 +86,5 @@ struct sw_flow *ovs_flow_tbl_lookup_ufid(struct flow_table *, + bool ovs_flow_cmp(const struct sw_flow *, const struct sw_flow_match *); + + void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, +- const struct sw_flow_mask *mask); ++ bool full, const struct sw_flow_mask *mask); + #endif /* flow_table.h */ +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index ed458b315ef4..7851b1222a36 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -229,6 +229,8 @@ struct packet_skb_cb { + } sa; + }; + ++#define vio_le() virtio_legacy_is_little_endian() ++ + #define PACKET_SKB_CB(__skb) ((struct packet_skb_cb *)((__skb)->cb)) + + #define GET_PBDQC_FROM_RB(x) ((struct tpacket_kbdq_core *)(&(x)->prb_bdqc)) +@@ -2561,15 +2563,15 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) + goto out_unlock; + + if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && +- (__virtio16_to_cpu(false, vnet_hdr.csum_start) + +- __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2 > +- __virtio16_to_cpu(false, vnet_hdr.hdr_len))) +- vnet_hdr.hdr_len = __cpu_to_virtio16(false, +- __virtio16_to_cpu(false, vnet_hdr.csum_start) + +- __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2); ++ (__virtio16_to_cpu(vio_le(), vnet_hdr.csum_start) + ++ __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset) + 2 > ++ __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len))) ++ vnet_hdr.hdr_len = __cpu_to_virtio16(vio_le(), ++ __virtio16_to_cpu(vio_le(), vnet_hdr.csum_start) + ++ __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset) + 2); + + err = -EINVAL; +- if (__virtio16_to_cpu(false, vnet_hdr.hdr_len) > len) ++ if (__virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len) > len) + goto out_unlock; + + if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) { +@@ -2612,7 +2614,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, +- __virtio16_to_cpu(false, vnet_hdr.hdr_len), ++ __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len), + msg->msg_flags & MSG_DONTWAIT, &err); + if (skb == NULL) + goto out_unlock; +@@ -2659,8 +2661,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) + + if (po->has_vnet_hdr) { + if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) { +- u16 s = __virtio16_to_cpu(false, vnet_hdr.csum_start); +- u16 o = __virtio16_to_cpu(false, vnet_hdr.csum_offset); ++ u16 s = __virtio16_to_cpu(vio_le(), vnet_hdr.csum_start); ++ u16 o = __virtio16_to_cpu(vio_le(), vnet_hdr.csum_offset); + if (!skb_partial_csum_set(skb, s, o)) { + err = -EINVAL; + goto out_free; +@@ -2668,7 +2670,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) + } + + skb_shinfo(skb)->gso_size = +- __virtio16_to_cpu(false, vnet_hdr.gso_size); ++ __virtio16_to_cpu(vio_le(), vnet_hdr.gso_size); + skb_shinfo(skb)->gso_type = gso_type; + + /* Header must be checked, and gso_segs computed. */ +@@ -3042,9 +3044,9 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + + /* This is a hint as to how much should be linear. */ + vnet_hdr.hdr_len = +- __cpu_to_virtio16(false, skb_headlen(skb)); ++ __cpu_to_virtio16(vio_le(), skb_headlen(skb)); + vnet_hdr.gso_size = +- __cpu_to_virtio16(false, sinfo->gso_size); ++ __cpu_to_virtio16(vio_le(), sinfo->gso_size); + if (sinfo->gso_type & SKB_GSO_TCPV4) + vnet_hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + else if (sinfo->gso_type & SKB_GSO_TCPV6) +@@ -3062,9 +3064,9 @@ static int packet_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; +- vnet_hdr.csum_start = __cpu_to_virtio16(false, ++ vnet_hdr.csum_start = __cpu_to_virtio16(vio_le(), + skb_checksum_start_offset(skb)); +- vnet_hdr.csum_offset = __cpu_to_virtio16(false, ++ vnet_hdr.csum_offset = __cpu_to_virtio16(vio_le(), + skb->csum_offset); + } else if (skb->ip_summed == CHECKSUM_UNNECESSARY) { + vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID; +diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c +index 715e01e5910a..f23a3b68bba6 100644 +--- a/net/sched/cls_fw.c ++++ b/net/sched/cls_fw.c +@@ -33,7 +33,6 @@ + + struct fw_head { + u32 mask; +- bool mask_set; + struct fw_filter __rcu *ht[HTSIZE]; + struct rcu_head rcu; + }; +@@ -84,7 +83,7 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, + } + } + } else { +- /* old method */ ++ /* Old method: classify the packet using its skb mark. */ + if (id && (TC_H_MAJ(id) == 0 || + !(TC_H_MAJ(id ^ tp->q->handle)))) { + res->classid = id; +@@ -114,14 +113,9 @@ static unsigned long fw_get(struct tcf_proto *tp, u32 handle) + + static int fw_init(struct tcf_proto *tp) + { +- struct fw_head *head; +- +- head = kzalloc(sizeof(struct fw_head), GFP_KERNEL); +- if (head == NULL) +- return -ENOBUFS; +- +- head->mask_set = false; +- rcu_assign_pointer(tp->root, head); ++ /* We don't allocate fw_head here, because in the old method ++ * we don't need it at all. ++ */ + return 0; + } + +@@ -252,7 +246,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, + int err; + + if (!opt) +- return handle ? -EINVAL : 0; ++ return handle ? -EINVAL : 0; /* Succeed if it is old method. */ + + err = nla_parse_nested(tb, TCA_FW_MAX, opt, fw_policy); + if (err < 0) +@@ -302,11 +296,17 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, + if (!handle) + return -EINVAL; + +- if (!head->mask_set) { +- head->mask = 0xFFFFFFFF; ++ if (!head) { ++ u32 mask = 0xFFFFFFFF; + if (tb[TCA_FW_MASK]) +- head->mask = nla_get_u32(tb[TCA_FW_MASK]); +- head->mask_set = true; ++ mask = nla_get_u32(tb[TCA_FW_MASK]); ++ ++ head = kzalloc(sizeof(*head), GFP_KERNEL); ++ if (!head) ++ return -ENOBUFS; ++ head->mask = mask; ++ ++ rcu_assign_pointer(tp->root, head); + } + + f = kzalloc(sizeof(struct fw_filter), GFP_KERNEL); +diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c +index 59e80356672b..3ac604f96da0 100644 +--- a/net/sctp/protocol.c ++++ b/net/sctp/protocol.c +@@ -1166,7 +1166,7 @@ static void sctp_v4_del_protocol(void) + unregister_inetaddr_notifier(&sctp_inetaddr_notifier); + } + +-static int __net_init sctp_net_init(struct net *net) ++static int __net_init sctp_defaults_init(struct net *net) + { + int status; + +@@ -1259,12 +1259,6 @@ static int __net_init sctp_net_init(struct net *net) + + sctp_dbg_objcnt_init(net); + +- /* Initialize the control inode/socket for handling OOTB packets. */ +- if ((status = sctp_ctl_sock_init(net))) { +- pr_err("Failed to initialize the SCTP control sock\n"); +- goto err_ctl_sock_init; +- } +- + /* Initialize the local address list. */ + INIT_LIST_HEAD(&net->sctp.local_addr_list); + spin_lock_init(&net->sctp.local_addr_lock); +@@ -1280,9 +1274,6 @@ static int __net_init sctp_net_init(struct net *net) + + return 0; + +-err_ctl_sock_init: +- sctp_dbg_objcnt_exit(net); +- sctp_proc_exit(net); + err_init_proc: + cleanup_sctp_mibs(net); + err_init_mibs: +@@ -1291,15 +1282,12 @@ err_sysctl_register: + return status; + } + +-static void __net_exit sctp_net_exit(struct net *net) ++static void __net_exit sctp_defaults_exit(struct net *net) + { + /* Free the local address list */ + sctp_free_addr_wq(net); + sctp_free_local_addr_list(net); + +- /* Free the control endpoint. */ +- inet_ctl_sock_destroy(net->sctp.ctl_sock); +- + sctp_dbg_objcnt_exit(net); + + sctp_proc_exit(net); +@@ -1307,9 +1295,32 @@ static void __net_exit sctp_net_exit(struct net *net) + sctp_sysctl_net_unregister(net); + } + +-static struct pernet_operations sctp_net_ops = { +- .init = sctp_net_init, +- .exit = sctp_net_exit, ++static struct pernet_operations sctp_defaults_ops = { ++ .init = sctp_defaults_init, ++ .exit = sctp_defaults_exit, ++}; ++ ++static int __net_init sctp_ctrlsock_init(struct net *net) ++{ ++ int status; ++ ++ /* Initialize the control inode/socket for handling OOTB packets. */ ++ status = sctp_ctl_sock_init(net); ++ if (status) ++ pr_err("Failed to initialize the SCTP control sock\n"); ++ ++ return status; ++} ++ ++static void __net_init sctp_ctrlsock_exit(struct net *net) ++{ ++ /* Free the control endpoint. */ ++ inet_ctl_sock_destroy(net->sctp.ctl_sock); ++} ++ ++static struct pernet_operations sctp_ctrlsock_ops = { ++ .init = sctp_ctrlsock_init, ++ .exit = sctp_ctrlsock_exit, + }; + + /* Initialize the universe into something sensible. */ +@@ -1442,8 +1453,11 @@ static __init int sctp_init(void) + sctp_v4_pf_init(); + sctp_v6_pf_init(); + +- status = sctp_v4_protosw_init(); ++ status = register_pernet_subsys(&sctp_defaults_ops); ++ if (status) ++ goto err_register_defaults; + ++ status = sctp_v4_protosw_init(); + if (status) + goto err_protosw_init; + +@@ -1451,9 +1465,9 @@ static __init int sctp_init(void) + if (status) + goto err_v6_protosw_init; + +- status = register_pernet_subsys(&sctp_net_ops); ++ status = register_pernet_subsys(&sctp_ctrlsock_ops); + if (status) +- goto err_register_pernet_subsys; ++ goto err_register_ctrlsock; + + status = sctp_v4_add_protocol(); + if (status) +@@ -1469,12 +1483,14 @@ out: + err_v6_add_protocol: + sctp_v4_del_protocol(); + err_add_protocol: +- unregister_pernet_subsys(&sctp_net_ops); +-err_register_pernet_subsys: ++ unregister_pernet_subsys(&sctp_ctrlsock_ops); ++err_register_ctrlsock: + sctp_v6_protosw_exit(); + err_v6_protosw_init: + sctp_v4_protosw_exit(); + err_protosw_init: ++ unregister_pernet_subsys(&sctp_defaults_ops); ++err_register_defaults: + sctp_v4_pf_exit(); + sctp_v6_pf_exit(); + sctp_sysctl_unregister(); +@@ -1507,12 +1523,14 @@ static __exit void sctp_exit(void) + sctp_v6_del_protocol(); + sctp_v4_del_protocol(); + +- unregister_pernet_subsys(&sctp_net_ops); ++ unregister_pernet_subsys(&sctp_ctrlsock_ops); + + /* Free protosw registrations */ + sctp_v6_protosw_exit(); + sctp_v4_protosw_exit(); + ++ unregister_pernet_subsys(&sctp_defaults_ops); ++ + /* Unregister with socket layer. */ + sctp_v6_pf_exit(); + sctp_v4_pf_exit(); diff --git a/patch/kernel/odroid-next/patch-4.2.3-4.patch b/patch/kernel/odroid-next/patch-4.2.3-4.patch new file mode 100644 index 000000000..e3600cf7d --- /dev/null +++ b/patch/kernel/odroid-next/patch-4.2.3-4.patch @@ -0,0 +1,9853 @@ +diff --git a/Documentation/HOWTO b/Documentation/HOWTO +index 93aa8604630e..21152d397b88 100644 +--- a/Documentation/HOWTO ++++ b/Documentation/HOWTO +@@ -218,16 +218,16 @@ The development process + Linux kernel development process currently consists of a few different + main kernel "branches" and lots of different subsystem-specific kernel + branches. These different branches are: +- - main 3.x kernel tree +- - 3.x.y -stable kernel tree +- - 3.x -git kernel patches ++ - main 4.x kernel tree ++ - 4.x.y -stable kernel tree ++ - 4.x -git kernel patches + - subsystem specific kernel trees and patches +- - the 3.x -next kernel tree for integration tests ++ - the 4.x -next kernel tree for integration tests + +-3.x kernel tree ++4.x kernel tree + ----------------- +-3.x kernels are maintained by Linus Torvalds, and can be found on +-kernel.org in the pub/linux/kernel/v3.x/ directory. Its development ++4.x kernels are maintained by Linus Torvalds, and can be found on ++kernel.org in the pub/linux/kernel/v4.x/ directory. Its development + process is as follows: + - As soon as a new kernel is released a two weeks window is open, + during this period of time maintainers can submit big diffs to +@@ -262,20 +262,20 @@ mailing list about kernel releases: + released according to perceived bug status, not according to a + preconceived timeline." + +-3.x.y -stable kernel tree ++4.x.y -stable kernel tree + --------------------------- + Kernels with 3-part versions are -stable kernels. They contain + relatively small and critical fixes for security problems or significant +-regressions discovered in a given 3.x kernel. ++regressions discovered in a given 4.x kernel. + + This is the recommended branch for users who want the most recent stable + kernel and are not interested in helping test development/experimental + versions. + +-If no 3.x.y kernel is available, then the highest numbered 3.x ++If no 4.x.y kernel is available, then the highest numbered 4.x + kernel is the current stable kernel. + +-3.x.y are maintained by the "stable" team , and ++4.x.y are maintained by the "stable" team , and + are released as needs dictate. The normal release period is approximately + two weeks, but it can be longer if there are no pressing problems. A + security-related problem, instead, can cause a release to happen almost +@@ -285,7 +285,7 @@ The file Documentation/stable_kernel_rules.txt in the kernel tree + documents what kinds of changes are acceptable for the -stable tree, and + how the release process works. + +-3.x -git patches ++4.x -git patches + ------------------ + These are daily snapshots of Linus' kernel tree which are managed in a + git repository (hence the name.) These patches are usually released +@@ -317,9 +317,9 @@ revisions to it, and maintainers can mark patches as under review, + accepted, or rejected. Most of these patchwork sites are listed at + http://patchwork.kernel.org/. + +-3.x -next kernel tree for integration tests ++4.x -next kernel tree for integration tests + --------------------------------------------- +-Before updates from subsystem trees are merged into the mainline 3.x ++Before updates from subsystem trees are merged into the mainline 4.x + tree, they need to be integration-tested. For this purpose, a special + testing repository exists into which virtually all subsystem trees are + pulled on an almost daily basis: +diff --git a/Makefile b/Makefile +index a6edbb11a69a..a952801a6cd5 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 2 +-SUBLEVEL = 3 ++SUBLEVEL = 4 + EXTRAVERSION = + NAME = Hurr durr I'ma sheep + +diff --git a/arch/arc/plat-axs10x/axs10x.c b/arch/arc/plat-axs10x/axs10x.c +index e7769c3ab5f2..ac79491ee2c0 100644 +--- a/arch/arc/plat-axs10x/axs10x.c ++++ b/arch/arc/plat-axs10x/axs10x.c +@@ -402,6 +402,8 @@ static void __init axs103_early_init(void) + unsigned int num_cores = (read_aux_reg(ARC_REG_MCIP_BCR) >> 16) & 0x3F; + if (num_cores > 2) + arc_set_core_freq(50 * 1000000); ++ else if (num_cores == 2) ++ arc_set_core_freq(75 * 1000000); + #endif + + switch (arc_get_core_freq()/1000000) { +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 7451b447cc2d..2c2b28ee4811 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -54,6 +54,14 @@ AS += -EL + LD += -EL + endif + ++# ++# The Scalar Replacement of Aggregates (SRA) optimization pass in GCC 4.9 and ++# later may result in code being generated that handles signed short and signed ++# char struct members incorrectly. So disable it. ++# (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65932) ++# ++KBUILD_CFLAGS += $(call cc-option,-fno-ipa-sra) ++ + # This selects which instruction set is used. + # Note that GCC does not numerically define an architecture version + # macro, but instead defines a whole series of macros which makes +diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi +index 534f27ceb10b..fa8107dec109 100644 +--- a/arch/arm/boot/dts/exynos5420.dtsi ++++ b/arch/arm/boot/dts/exynos5420.dtsi +@@ -1118,7 +1118,7 @@ + interrupt-parent = <&combiner>; + interrupts = <3 0>; + clock-names = "sysmmu", "master"; +- clocks = <&clock CLK_SMMU_FIMD1M0>, <&clock CLK_FIMD1>; ++ clocks = <&clock CLK_SMMU_FIMD1M1>, <&clock CLK_FIMD1>; + power-domains = <&disp_pd>; + #iommu-cells = <0>; + }; +diff --git a/arch/arm/boot/dts/imx6qdl-rex.dtsi b/arch/arm/boot/dts/imx6qdl-rex.dtsi +index 3373fd958e95..a50356243888 100644 +--- a/arch/arm/boot/dts/imx6qdl-rex.dtsi ++++ b/arch/arm/boot/dts/imx6qdl-rex.dtsi +@@ -35,7 +35,6 @@ + compatible = "regulator-fixed"; + reg = <1>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbh1>; + regulator-name = "usbh1_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; +@@ -47,7 +46,6 @@ + compatible = "regulator-fixed"; + reg = <2>; + pinctrl-names = "default"; +- pinctrl-0 = <&pinctrl_usbotg>; + regulator-name = "usb_otg_vbus"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; +diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts +index a5474113cd50..67659a0ed13e 100644 +--- a/arch/arm/boot/dts/omap3-beagle.dts ++++ b/arch/arm/boot/dts/omap3-beagle.dts +@@ -202,7 +202,7 @@ + + tfp410_pins: pinmux_tfp410_pins { + pinctrl-single,pins = < +- 0x194 (PIN_OUTPUT | MUX_MODE4) /* hdq_sio.gpio_170 */ ++ 0x196 (PIN_OUTPUT | MUX_MODE4) /* hdq_sio.gpio_170 */ + >; + }; + +diff --git a/arch/arm/boot/dts/omap5-uevm.dts b/arch/arm/boot/dts/omap5-uevm.dts +index 275618f19a43..5771a149ce4a 100644 +--- a/arch/arm/boot/dts/omap5-uevm.dts ++++ b/arch/arm/boot/dts/omap5-uevm.dts +@@ -174,8 +174,8 @@ + + i2c5_pins: pinmux_i2c5_pins { + pinctrl-single,pins = < +- 0x184 (PIN_INPUT | MUX_MODE0) /* i2c5_scl */ +- 0x186 (PIN_INPUT | MUX_MODE0) /* i2c5_sda */ ++ 0x186 (PIN_INPUT | MUX_MODE0) /* i2c5_scl */ ++ 0x188 (PIN_INPUT | MUX_MODE0) /* i2c5_sda */ + >; + }; + +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 6a63f30c9a69..f5f384c04335 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -107,7 +107,7 @@ + 720000 1200000 + 528000 1100000 + 312000 1000000 +- 144000 900000 ++ 144000 1000000 + >; + #cooling-cells = <2>; + cooling-min-level = <0>; +diff --git a/arch/arm/kernel/kgdb.c b/arch/arm/kernel/kgdb.c +index a6ad93c9bce3..fd9eefce0a7b 100644 +--- a/arch/arm/kernel/kgdb.c ++++ b/arch/arm/kernel/kgdb.c +@@ -259,15 +259,17 @@ int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt) + if (err) + return err; + +- patch_text((void *)bpt->bpt_addr, +- *(unsigned int *)arch_kgdb_ops.gdb_bpt_instr); ++ /* Machine is already stopped, so we can use __patch_text() directly */ ++ __patch_text((void *)bpt->bpt_addr, ++ *(unsigned int *)arch_kgdb_ops.gdb_bpt_instr); + + return err; + } + + int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt) + { +- patch_text((void *)bpt->bpt_addr, *(unsigned int *)bpt->saved_instr); ++ /* Machine is already stopped, so we can use __patch_text() directly */ ++ __patch_text((void *)bpt->bpt_addr, *(unsigned int *)bpt->saved_instr); + + return 0; + } +diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c +index 54272e0be713..7d5379c1c443 100644 +--- a/arch/arm/kernel/perf_event.c ++++ b/arch/arm/kernel/perf_event.c +@@ -795,8 +795,10 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu) + + /* Don't bother with PPIs; they're already affine */ + irq = platform_get_irq(pdev, 0); +- if (irq >= 0 && irq_is_percpu(irq)) ++ if (irq >= 0 && irq_is_percpu(irq)) { ++ cpumask_setall(&pmu->supported_cpus); + return 0; ++ } + + irqs = kcalloc(pdev->num_resources, sizeof(*irqs), GFP_KERNEL); + if (!irqs) +diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c +index 423663e23791..586eef26203d 100644 +--- a/arch/arm/kernel/signal.c ++++ b/arch/arm/kernel/signal.c +@@ -343,12 +343,17 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, + */ + thumb = handler & 1; + +-#if __LINUX_ARM_ARCH__ >= 7 ++#if __LINUX_ARM_ARCH__ >= 6 + /* +- * Clear the If-Then Thumb-2 execution state +- * ARM spec requires this to be all 000s in ARM mode +- * Snapdragon S4/Krait misbehaves on a Thumb=>ARM +- * signal transition without this. ++ * Clear the If-Then Thumb-2 execution state. ARM spec ++ * requires this to be all 000s in ARM mode. Snapdragon ++ * S4/Krait misbehaves on a Thumb=>ARM signal transition ++ * without this. ++ * ++ * We must do this whenever we are running on a Thumb-2 ++ * capable CPU, which includes ARMv6T2. However, we elect ++ * to do this whenever we're on an ARMv6 or later CPU for ++ * simplicity. + */ + cpsr &= ~PSR_IT_MASK; + #endif +diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S +index 702740d37465..51a59504bef4 100644 +--- a/arch/arm/kvm/interrupts_head.S ++++ b/arch/arm/kvm/interrupts_head.S +@@ -515,8 +515,7 @@ ARM_BE8(rev r6, r6 ) + + mrc p15, 0, r2, c14, c3, 1 @ CNTV_CTL + str r2, [vcpu, #VCPU_TIMER_CNTV_CTL] +- bic r2, #1 @ Clear ENABLE +- mcr p15, 0, r2, c14, c3, 1 @ CNTV_CTL ++ + isb + + mrrc p15, 3, rr_lo_hi(r2, r3), c14 @ CNTV_CVAL +@@ -529,6 +528,9 @@ ARM_BE8(rev r6, r6 ) + mcrr p15, 4, r2, r2, c14 @ CNTVOFF + + 1: ++ mov r2, #0 @ Clear ENABLE ++ mcr p15, 0, r2, c14, c3, 1 @ CNTV_CTL ++ + @ Allow physical timer/counter access for the host + mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL + orr r2, r2, #(CNTHCTL_PL1PCEN | CNTHCTL_PL1PCTEN) +diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c +index 7b4201294187..6984342da13d 100644 +--- a/arch/arm/kvm/mmu.c ++++ b/arch/arm/kvm/mmu.c +@@ -1792,8 +1792,10 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, + if (vma->vm_flags & VM_PFNMAP) { + gpa_t gpa = mem->guest_phys_addr + + (vm_start - mem->userspace_addr); +- phys_addr_t pa = (vma->vm_pgoff << PAGE_SHIFT) + +- vm_start - vma->vm_start; ++ phys_addr_t pa; ++ ++ pa = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; ++ pa += vm_start - vma->vm_start; + + /* IO region dirty page logging not allowed */ + if (memslot->flags & KVM_MEM_LOG_DIRTY_PAGES) +diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c +index 9bdf54795f05..56978199c479 100644 +--- a/arch/arm/mach-exynos/mcpm-exynos.c ++++ b/arch/arm/mach-exynos/mcpm-exynos.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include "regs-pmu.h" + #include "common.h" +@@ -70,7 +71,31 @@ static int exynos_cpu_powerup(unsigned int cpu, unsigned int cluster) + cluster >= EXYNOS5420_NR_CLUSTERS) + return -EINVAL; + +- exynos_cpu_power_up(cpunr); ++ if (!exynos_cpu_power_state(cpunr)) { ++ exynos_cpu_power_up(cpunr); ++ ++ /* ++ * This assumes the cluster number of the big cores(Cortex A15) ++ * is 0 and the Little cores(Cortex A7) is 1. ++ * When the system was booted from the Little core, ++ * they should be reset during power up cpu. ++ */ ++ if (cluster && ++ cluster == MPIDR_AFFINITY_LEVEL(cpu_logical_map(0), 1)) { ++ /* ++ * Before we reset the Little cores, we should wait ++ * the SPARE2 register is set to 1 because the init ++ * codes of the iROM will set the register after ++ * initialization. ++ */ ++ while (!pmu_raw_readl(S5P_PMU_SPARE2)) ++ udelay(10); ++ ++ pmu_raw_writel(EXYNOS5420_KFC_CORE_RESET(cpu), ++ EXYNOS_SWRESET); ++ } ++ } ++ + return 0; + } + +diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h +index b7614333d296..fba9068ed260 100644 +--- a/arch/arm/mach-exynos/regs-pmu.h ++++ b/arch/arm/mach-exynos/regs-pmu.h +@@ -513,6 +513,12 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr) + #define SPREAD_ENABLE 0xF + #define SPREAD_USE_STANDWFI 0xF + ++#define EXYNOS5420_KFC_CORE_RESET0 BIT(8) ++#define EXYNOS5420_KFC_ETM_RESET0 BIT(20) ++ ++#define EXYNOS5420_KFC_CORE_RESET(_nr) \ ++ ((EXYNOS5420_KFC_CORE_RESET0 | EXYNOS5420_KFC_ETM_RESET0) << (_nr)) ++ + #define EXYNOS5420_BB_CON1 0x0784 + #define EXYNOS5420_BB_SEL_EN BIT(31) + #define EXYNOS5420_BB_PMOS_EN BIT(7) +diff --git a/arch/arm/plat-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c +index ad9529cc4203..daa1a65f2eb7 100644 +--- a/arch/arm/plat-pxa/ssp.c ++++ b/arch/arm/plat-pxa/ssp.c +@@ -107,7 +107,6 @@ static const struct of_device_id pxa_ssp_of_ids[] = { + { .compatible = "mvrl,pxa168-ssp", .data = (void *) PXA168_SSP }, + { .compatible = "mrvl,pxa910-ssp", .data = (void *) PXA910_SSP }, + { .compatible = "mrvl,ce4100-ssp", .data = (void *) CE4100_SSP }, +- { .compatible = "mrvl,lpss-ssp", .data = (void *) LPSS_SSP }, + { }, + }; + MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids); +diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c +index e8ca6eaedd02..13671a9cf016 100644 +--- a/arch/arm64/kernel/efi.c ++++ b/arch/arm64/kernel/efi.c +@@ -258,7 +258,8 @@ static bool __init efi_virtmap_init(void) + */ + if (!is_normal_ram(md)) + prot = __pgprot(PROT_DEVICE_nGnRE); +- else if (md->type == EFI_RUNTIME_SERVICES_CODE) ++ else if (md->type == EFI_RUNTIME_SERVICES_CODE || ++ !PAGE_ALIGNED(md->phys_addr)) + prot = PAGE_KERNEL_EXEC; + else + prot = PAGE_KERNEL; +diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S +index 08cafc518b9a..0f03a8fe2314 100644 +--- a/arch/arm64/kernel/entry-ftrace.S ++++ b/arch/arm64/kernel/entry-ftrace.S +@@ -178,6 +178,24 @@ ENTRY(ftrace_stub) + ENDPROC(ftrace_stub) + + #ifdef CONFIG_FUNCTION_GRAPH_TRACER ++ /* save return value regs*/ ++ .macro save_return_regs ++ sub sp, sp, #64 ++ stp x0, x1, [sp] ++ stp x2, x3, [sp, #16] ++ stp x4, x5, [sp, #32] ++ stp x6, x7, [sp, #48] ++ .endm ++ ++ /* restore return value regs*/ ++ .macro restore_return_regs ++ ldp x0, x1, [sp] ++ ldp x2, x3, [sp, #16] ++ ldp x4, x5, [sp, #32] ++ ldp x6, x7, [sp, #48] ++ add sp, sp, #64 ++ .endm ++ + /* + * void ftrace_graph_caller(void) + * +@@ -204,11 +222,11 @@ ENDPROC(ftrace_graph_caller) + * only when CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST is enabled. + */ + ENTRY(return_to_handler) +- str x0, [sp, #-16]! ++ save_return_regs + mov x0, x29 // parent's fp + bl ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); + mov x30, x0 // restore the original return address +- ldr x0, [sp], #16 ++ restore_return_regs + ret + END(return_to_handler) + #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c +index 94d98cd1aad8..27c3e6fd24c1 100644 +--- a/arch/arm64/mm/fault.c ++++ b/arch/arm64/mm/fault.c +@@ -278,6 +278,7 @@ retry: + * starvation. + */ + mm_flags &= ~FAULT_FLAG_ALLOW_RETRY; ++ mm_flags |= FAULT_FLAG_TRIED; + goto retry; + } + } +diff --git a/arch/m68k/include/asm/linkage.h b/arch/m68k/include/asm/linkage.h +index 5a822bb790f7..066e74f666ae 100644 +--- a/arch/m68k/include/asm/linkage.h ++++ b/arch/m68k/include/asm/linkage.h +@@ -4,4 +4,34 @@ + #define __ALIGN .align 4 + #define __ALIGN_STR ".align 4" + ++/* ++ * Make sure the compiler doesn't do anything stupid with the ++ * arguments on the stack - they are owned by the *caller*, not ++ * the callee. This just fools gcc into not spilling into them, ++ * and keeps it from doing tailcall recursion and/or using the ++ * stack slots for temporaries, since they are live and "used" ++ * all the way to the end of the function. ++ */ ++#define asmlinkage_protect(n, ret, args...) \ ++ __asmlinkage_protect##n(ret, ##args) ++#define __asmlinkage_protect_n(ret, args...) \ ++ __asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args) ++#define __asmlinkage_protect0(ret) \ ++ __asmlinkage_protect_n(ret) ++#define __asmlinkage_protect1(ret, arg1) \ ++ __asmlinkage_protect_n(ret, "m" (arg1)) ++#define __asmlinkage_protect2(ret, arg1, arg2) \ ++ __asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2)) ++#define __asmlinkage_protect3(ret, arg1, arg2, arg3) \ ++ __asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3)) ++#define __asmlinkage_protect4(ret, arg1, arg2, arg3, arg4) \ ++ __asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \ ++ "m" (arg4)) ++#define __asmlinkage_protect5(ret, arg1, arg2, arg3, arg4, arg5) \ ++ __asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \ ++ "m" (arg4), "m" (arg5)) ++#define __asmlinkage_protect6(ret, arg1, arg2, arg3, arg4, arg5, arg6) \ ++ __asmlinkage_protect_n(ret, "m" (arg1), "m" (arg2), "m" (arg3), \ ++ "m" (arg4), "m" (arg5), "m" (arg6)) ++ + #endif +diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S +index 9f71c06aebf6..209ded16806b 100644 +--- a/arch/mips/kernel/cps-vec.S ++++ b/arch/mips/kernel/cps-vec.S +@@ -39,6 +39,7 @@ + mfc0 \dest, CP0_CONFIG, 3 + andi \dest, \dest, MIPS_CONF3_MT + beqz \dest, \nomt ++ nop + .endm + + .section .text.cps-vec +@@ -223,10 +224,9 @@ LEAF(excep_ejtag) + END(excep_ejtag) + + LEAF(mips_cps_core_init) +-#ifdef CONFIG_MIPS_MT ++#ifdef CONFIG_MIPS_MT_SMP + /* Check that the core implements the MT ASE */ + has_mt t0, 3f +- nop + + .set push + .set mips64r2 +@@ -310,8 +310,9 @@ LEAF(mips_cps_boot_vpes) + PTR_ADDU t0, t0, t1 + + /* Calculate this VPEs ID. If the core doesn't support MT use 0 */ ++ li t9, 0 ++#ifdef CONFIG_MIPS_MT_SMP + has_mt ta2, 1f +- li t9, 0 + + /* Find the number of VPEs present in the core */ + mfc0 t1, CP0_MVPCONF0 +@@ -330,6 +331,7 @@ LEAF(mips_cps_boot_vpes) + /* Retrieve the VPE ID from EBase.CPUNum */ + mfc0 t9, $15, 1 + and t9, t9, t1 ++#endif + + 1: /* Calculate a pointer to this VPEs struct vpe_boot_config */ + li t1, VPEBOOTCFG_SIZE +@@ -337,7 +339,7 @@ LEAF(mips_cps_boot_vpes) + PTR_L ta3, COREBOOTCFG_VPECONFIG(t0) + PTR_ADDU v0, v0, ta3 + +-#ifdef CONFIG_MIPS_MT ++#ifdef CONFIG_MIPS_MT_SMP + + /* If the core doesn't support MT then return */ + bnez ta2, 1f +@@ -451,7 +453,7 @@ LEAF(mips_cps_boot_vpes) + + 2: .set pop + +-#endif /* CONFIG_MIPS_MT */ ++#endif /* CONFIG_MIPS_MT_SMP */ + + /* Return */ + jr ra +diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c +index 008b3378653a..4ceac5cdd6b8 100644 +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -338,7 +338,7 @@ static void __init bootmem_init(void) + if (end <= reserved_end) + continue; + #ifdef CONFIG_BLK_DEV_INITRD +- /* mapstart should be after initrd_end */ ++ /* Skip zones before initrd and initrd itself */ + if (initrd_end && end <= (unsigned long)PFN_UP(__pa(initrd_end))) + continue; + #endif +@@ -371,6 +371,14 @@ static void __init bootmem_init(void) + max_low_pfn = PFN_DOWN(HIGHMEM_START); + } + ++#ifdef CONFIG_BLK_DEV_INITRD ++ /* ++ * mapstart should be after initrd_end ++ */ ++ if (initrd_end) ++ mapstart = max(mapstart, (unsigned long)PFN_UP(__pa(initrd_end))); ++#endif ++ + /* + * Initialize the boot-time allocator with low memory only. + */ +diff --git a/arch/mips/loongson64/common/env.c b/arch/mips/loongson64/common/env.c +index f6c44dd332e2..d6d07ad56180 100644 +--- a/arch/mips/loongson64/common/env.c ++++ b/arch/mips/loongson64/common/env.c +@@ -64,6 +64,9 @@ void __init prom_init_env(void) + } + if (memsize == 0) + memsize = 256; ++ ++ loongson_sysconf.nr_uarts = 1; ++ + pr_info("memsize=%u, highmemsize=%u\n", memsize, highmemsize); + #else + struct boot_params *boot_p; +diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c +index eeaf0245c3b1..815892ed3fe8 100644 +--- a/arch/mips/mm/dma-default.c ++++ b/arch/mips/mm/dma-default.c +@@ -100,7 +100,7 @@ static gfp_t massage_gfp_flags(const struct device *dev, gfp_t gfp) + else + #endif + #if defined(CONFIG_ZONE_DMA) && !defined(CONFIG_ZONE_DMA32) +- if (dev->coherent_dma_mask < DMA_BIT_MASK(64)) ++ if (dev->coherent_dma_mask < DMA_BIT_MASK(sizeof(phys_addr_t) * 8)) + dma_flag = __GFP_DMA; + else + #endif +diff --git a/arch/mips/net/bpf_jit_asm.S b/arch/mips/net/bpf_jit_asm.S +index e92726099be0..dabf4179cd7e 100644 +--- a/arch/mips/net/bpf_jit_asm.S ++++ b/arch/mips/net/bpf_jit_asm.S +@@ -64,8 +64,20 @@ sk_load_word_positive: + PTR_ADDU t1, $r_skb_data, offset + lw $r_A, 0(t1) + #ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) + wsbh t0, $r_A + rotr $r_A, t0, 16 ++# else ++ sll t0, $r_A, 24 ++ srl t1, $r_A, 24 ++ srl t2, $r_A, 8 ++ or t0, t0, t1 ++ andi t2, t2, 0xff00 ++ andi t1, $r_A, 0xff00 ++ or t0, t0, t2 ++ sll t1, t1, 8 ++ or $r_A, t0, t1 ++# endif + #endif + jr $r_ra + move $r_ret, zero +@@ -80,8 +92,16 @@ sk_load_half_positive: + PTR_ADDU t1, $r_skb_data, offset + lh $r_A, 0(t1) + #ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) + wsbh t0, $r_A + seh $r_A, t0 ++# else ++ sll t0, $r_A, 24 ++ andi t1, $r_A, 0xff00 ++ sra t0, t0, 16 ++ srl t1, t1, 8 ++ or $r_A, t0, t1 ++# endif + #endif + jr $r_ra + move $r_ret, zero +@@ -148,23 +168,47 @@ sk_load_byte_positive: + NESTED(bpf_slow_path_word, (6 * SZREG), $r_sp) + bpf_slow_path_common(4) + #ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) + wsbh t0, $r_s0 + jr $r_ra + rotr $r_A, t0, 16 +-#endif ++# else ++ sll t0, $r_s0, 24 ++ srl t1, $r_s0, 24 ++ srl t2, $r_s0, 8 ++ or t0, t0, t1 ++ andi t2, t2, 0xff00 ++ andi t1, $r_s0, 0xff00 ++ or t0, t0, t2 ++ sll t1, t1, 8 ++ jr $r_ra ++ or $r_A, t0, t1 ++# endif ++#else + jr $r_ra +- move $r_A, $r_s0 ++ move $r_A, $r_s0 ++#endif + + END(bpf_slow_path_word) + + NESTED(bpf_slow_path_half, (6 * SZREG), $r_sp) + bpf_slow_path_common(2) + #ifdef CONFIG_CPU_LITTLE_ENDIAN ++# if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) + jr $r_ra + wsbh $r_A, $r_s0 +-#endif ++# else ++ sll t0, $r_s0, 8 ++ andi t1, $r_s0, 0xff00 ++ andi t0, t0, 0xff00 ++ srl t1, t1, 8 ++ jr $r_ra ++ or $r_A, t0, t1 ++# endif ++#else + jr $r_ra + move $r_A, $r_s0 ++#endif + + END(bpf_slow_path_half) + +diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c +index 05ea8fc7f829..4816fe2fa857 100644 +--- a/arch/powerpc/kvm/book3s.c ++++ b/arch/powerpc/kvm/book3s.c +@@ -827,12 +827,15 @@ int kvmppc_h_logical_ci_load(struct kvm_vcpu *vcpu) + unsigned long size = kvmppc_get_gpr(vcpu, 4); + unsigned long addr = kvmppc_get_gpr(vcpu, 5); + u64 buf; ++ int srcu_idx; + int ret; + + if (!is_power_of_2(size) || (size > sizeof(buf))) + return H_TOO_HARD; + ++ srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); + ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, addr, size, &buf); ++ srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); + if (ret != 0) + return H_TOO_HARD; + +@@ -867,6 +870,7 @@ int kvmppc_h_logical_ci_store(struct kvm_vcpu *vcpu) + unsigned long addr = kvmppc_get_gpr(vcpu, 5); + unsigned long val = kvmppc_get_gpr(vcpu, 6); + u64 buf; ++ int srcu_idx; + int ret; + + switch (size) { +@@ -890,7 +894,9 @@ int kvmppc_h_logical_ci_store(struct kvm_vcpu *vcpu) + return H_TOO_HARD; + } + ++ srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); + ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, addr, size, &buf); ++ srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); + if (ret != 0) + return H_TOO_HARD; + +diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c +index 68d067ad4222..a9f753fb73a8 100644 +--- a/arch/powerpc/kvm/book3s_hv.c ++++ b/arch/powerpc/kvm/book3s_hv.c +@@ -2178,7 +2178,7 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) + vc->runner = vcpu; + if (n_ceded == vc->n_runnable) { + kvmppc_vcore_blocked(vc); +- } else if (should_resched()) { ++ } else if (need_resched()) { + vc->vcore_state = VCORE_PREEMPT; + /* Let something else run */ + cond_resched_lock(&vc->lock); +diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S +index 76408cf0ad04..437f64350847 100644 +--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S ++++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S +@@ -1171,6 +1171,7 @@ mc_cont: + bl kvmhv_accumulate_time + #endif + ++ mr r3, r12 + /* Increment exit count, poke other threads to exit */ + bl kvmhv_commence_exit + nop +diff --git a/arch/powerpc/platforms/pasemi/msi.c b/arch/powerpc/platforms/pasemi/msi.c +index 27f2b187a91b..ff1bb4b690b9 100644 +--- a/arch/powerpc/platforms/pasemi/msi.c ++++ b/arch/powerpc/platforms/pasemi/msi.c +@@ -63,6 +63,7 @@ static struct irq_chip mpic_pasemi_msi_chip = { + static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev) + { + struct msi_desc *entry; ++ irq_hw_number_t hwirq; + + pr_debug("pasemi_msi_teardown_msi_irqs, pdev %p\n", pdev); + +@@ -70,10 +71,10 @@ static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev) + if (entry->irq == NO_IRQ) + continue; + ++ hwirq = virq_to_hw(entry->irq); + irq_set_msi_desc(entry->irq, NULL); +- msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, +- virq_to_hw(entry->irq), ALLOC_CHUNK); + irq_dispose_mapping(entry->irq); ++ msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq, ALLOC_CHUNK); + } + + return; +diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c +index 765d8ed558d0..fd16f86e54a9 100644 +--- a/arch/powerpc/platforms/powernv/pci.c ++++ b/arch/powerpc/platforms/powernv/pci.c +@@ -99,6 +99,7 @@ void pnv_teardown_msi_irqs(struct pci_dev *pdev) + struct pci_controller *hose = pci_bus_to_host(pdev->bus); + struct pnv_phb *phb = hose->private_data; + struct msi_desc *entry; ++ irq_hw_number_t hwirq; + + if (WARN_ON(!phb)) + return; +@@ -106,10 +107,10 @@ void pnv_teardown_msi_irqs(struct pci_dev *pdev) + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; ++ hwirq = virq_to_hw(entry->irq); + irq_set_msi_desc(entry->irq, NULL); +- msi_bitmap_free_hwirqs(&phb->msi_bmp, +- virq_to_hw(entry->irq) - phb->msi_base, 1); + irq_dispose_mapping(entry->irq); ++ msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, 1); + } + } + #endif /* CONFIG_PCI_MSI */ +diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c +index 5236e5427c38..691e8e517b3e 100644 +--- a/arch/powerpc/sysdev/fsl_msi.c ++++ b/arch/powerpc/sysdev/fsl_msi.c +@@ -128,15 +128,16 @@ static void fsl_teardown_msi_irqs(struct pci_dev *pdev) + { + struct msi_desc *entry; + struct fsl_msi *msi_data; ++ irq_hw_number_t hwirq; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; ++ hwirq = virq_to_hw(entry->irq); + msi_data = irq_get_chip_data(entry->irq); + irq_set_msi_desc(entry->irq, NULL); +- msi_bitmap_free_hwirqs(&msi_data->bitmap, +- virq_to_hw(entry->irq), 1); + irq_dispose_mapping(entry->irq); ++ msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1); + } + + return; +diff --git a/arch/powerpc/sysdev/mpic_u3msi.c b/arch/powerpc/sysdev/mpic_u3msi.c +index fc46ef3b816e..4c3165fa521c 100644 +--- a/arch/powerpc/sysdev/mpic_u3msi.c ++++ b/arch/powerpc/sysdev/mpic_u3msi.c +@@ -107,15 +107,16 @@ static u64 find_u4_magic_addr(struct pci_dev *pdev, unsigned int hwirq) + static void u3msi_teardown_msi_irqs(struct pci_dev *pdev) + { + struct msi_desc *entry; ++ irq_hw_number_t hwirq; + + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; + ++ hwirq = virq_to_hw(entry->irq); + irq_set_msi_desc(entry->irq, NULL); +- msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, +- virq_to_hw(entry->irq), 1); + irq_dispose_mapping(entry->irq); ++ msi_bitmap_free_hwirqs(&msi_mpic->msi_bitmap, hwirq, 1); + } + + return; +diff --git a/arch/powerpc/sysdev/ppc4xx_msi.c b/arch/powerpc/sysdev/ppc4xx_msi.c +index 6eb21f2ea585..060f23775255 100644 +--- a/arch/powerpc/sysdev/ppc4xx_msi.c ++++ b/arch/powerpc/sysdev/ppc4xx_msi.c +@@ -124,16 +124,17 @@ void ppc4xx_teardown_msi_irqs(struct pci_dev *dev) + { + struct msi_desc *entry; + struct ppc4xx_msi *msi_data = &ppc4xx_msi; ++ irq_hw_number_t hwirq; + + dev_dbg(&dev->dev, "PCIE-MSI: tearing down msi irqs\n"); + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq == NO_IRQ) + continue; ++ hwirq = virq_to_hw(entry->irq); + irq_set_msi_desc(entry->irq, NULL); +- msi_bitmap_free_hwirqs(&msi_data->bitmap, +- virq_to_hw(entry->irq), 1); + irq_dispose_mapping(entry->irq); ++ msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1); + } + } + +diff --git a/arch/s390/boot/compressed/Makefile b/arch/s390/boot/compressed/Makefile +index d4788111c161..fac6ac9790fa 100644 +--- a/arch/s390/boot/compressed/Makefile ++++ b/arch/s390/boot/compressed/Makefile +@@ -10,7 +10,7 @@ targets += misc.o piggy.o sizes.h head.o + + KBUILD_CFLAGS := -m64 -D__KERNEL__ $(LINUX_INCLUDE) -O2 + KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING +-KBUILD_CFLAGS += $(cflags-y) -fno-delete-null-pointer-checks ++KBUILD_CFLAGS += $(cflags-y) -fno-delete-null-pointer-checks -msoft-float + KBUILD_CFLAGS += $(call cc-option,-mpacked-stack) + KBUILD_CFLAGS += $(call cc-option,-ffreestanding) + +diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c +index fe8d6924efaa..c78ba51ae285 100644 +--- a/arch/s390/kernel/compat_signal.c ++++ b/arch/s390/kernel/compat_signal.c +@@ -48,6 +48,19 @@ typedef struct + struct ucontext32 uc; + } rt_sigframe32; + ++static inline void sigset_to_sigset32(unsigned long *set64, ++ compat_sigset_word *set32) ++{ ++ set32[0] = (compat_sigset_word) set64[0]; ++ set32[1] = (compat_sigset_word)(set64[0] >> 32); ++} ++ ++static inline void sigset32_to_sigset(compat_sigset_word *set32, ++ unsigned long *set64) ++{ ++ set64[0] = (unsigned long) set32[0] | ((unsigned long) set32[1] << 32); ++} ++ + int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) + { + int err; +@@ -303,10 +316,12 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) + { + struct pt_regs *regs = task_pt_regs(current); + sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; ++ compat_sigset_t cset; + sigset_t set; + +- if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) ++ if (__copy_from_user(&cset.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) + goto badframe; ++ sigset32_to_sigset(cset.sig, set.sig); + set_current_blocked(&set); + if (restore_sigregs32(regs, &frame->sregs)) + goto badframe; +@@ -323,10 +338,12 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) + { + struct pt_regs *regs = task_pt_regs(current); + rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; ++ compat_sigset_t cset; + sigset_t set; + +- if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) ++ if (__copy_from_user(&cset, &frame->uc.uc_sigmask, sizeof(cset))) + goto badframe; ++ sigset32_to_sigset(cset.sig, set.sig); + set_current_blocked(&set); + if (compat_restore_altstack(&frame->uc.uc_stack)) + goto badframe; +@@ -397,7 +414,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, + return -EFAULT; + + /* Create struct sigcontext32 on the signal stack */ +- memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32); ++ sigset_to_sigset32(set->sig, sc.oldmask); + sc.sregs = (__u32)(unsigned long __force) &frame->sregs; + if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) + return -EFAULT; +@@ -458,6 +475,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, + static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, + struct pt_regs *regs) + { ++ compat_sigset_t cset; + rt_sigframe32 __user *frame; + unsigned long restorer; + size_t frame_size; +@@ -505,11 +523,12 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, + store_sigregs(); + + /* Create ucontext on the signal stack. */ ++ sigset_to_sigset32(set->sig, cset.sig); + if (__put_user(uc_flags, &frame->uc.uc_flags) || + __put_user(0, &frame->uc.uc_link) || + __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || + save_sigregs32(regs, &frame->uc.uc_mcontext) || +- __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || ++ __copy_to_user(&frame->uc.uc_sigmask, &cset, sizeof(cset)) || + save_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) + return -EFAULT; + +diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S +index 8cb3e438f21e..d330840a2b18 100644 +--- a/arch/x86/entry/entry_64.S ++++ b/arch/x86/entry/entry_64.S +@@ -1219,7 +1219,18 @@ END(error_exit) + + /* Runs on exception stack */ + ENTRY(nmi) ++ /* ++ * Fix up the exception frame if we're on Xen. ++ * PARAVIRT_ADJUST_EXCEPTION_FRAME is guaranteed to push at most ++ * one value to the stack on native, so it may clobber the rdx ++ * scratch slot, but it won't clobber any of the important ++ * slots past it. ++ * ++ * Xen is a different story, because the Xen frame itself overlaps ++ * the "NMI executing" variable. ++ */ + PARAVIRT_ADJUST_EXCEPTION_FRAME ++ + /* + * We allow breakpoints in NMIs. If a breakpoint occurs, then + * the iretq it performs will take us out of NMI context. +@@ -1270,9 +1281,12 @@ ENTRY(nmi) + * we don't want to enable interrupts, because then we'll end + * up in an awkward situation in which IRQs are on but NMIs + * are off. ++ * ++ * We also must not push anything to the stack before switching ++ * stacks lest we corrupt the "NMI executing" variable. + */ + +- SWAPGS ++ SWAPGS_UNSAFE_STACK + cld + movq %rsp, %rdx + movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp +diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h +index 9ebc3d009373..2350ab78183a 100644 +--- a/arch/x86/include/asm/msr-index.h ++++ b/arch/x86/include/asm/msr-index.h +@@ -311,6 +311,7 @@ + /* C1E active bits in int pending message */ + #define K8_INTP_C1E_ACTIVE_MASK 0x18000000 + #define MSR_K8_TSEG_ADDR 0xc0010112 ++#define MSR_K8_TSEG_MASK 0xc0010113 + #define K8_MTRRFIXRANGE_DRAM_ENABLE 0x00040000 /* MtrrFixDramEn bit */ + #define K8_MTRRFIXRANGE_DRAM_MODIFY 0x00080000 /* MtrrFixDramModEn bit */ + #define K8_MTRR_RDMEM_WRMEM_MASK 0x18181818 /* Mask: RdMem|WrMem */ +diff --git a/arch/x86/include/asm/preempt.h b/arch/x86/include/asm/preempt.h +index dca71714f860..b12f81022a6b 100644 +--- a/arch/x86/include/asm/preempt.h ++++ b/arch/x86/include/asm/preempt.h +@@ -90,9 +90,9 @@ static __always_inline bool __preempt_count_dec_and_test(void) + /* + * Returns true when we need to resched and can (barring IRQ state). + */ +-static __always_inline bool should_resched(void) ++static __always_inline bool should_resched(int preempt_offset) + { +- return unlikely(!raw_cpu_read_4(__preempt_count)); ++ return unlikely(raw_cpu_read_4(__preempt_count) == preempt_offset); + } + + #ifdef CONFIG_PREEMPT +diff --git a/arch/x86/include/asm/qspinlock.h b/arch/x86/include/asm/qspinlock.h +index 9d51fae1cba3..eaba08076030 100644 +--- a/arch/x86/include/asm/qspinlock.h ++++ b/arch/x86/include/asm/qspinlock.h +@@ -39,18 +39,27 @@ static inline void queued_spin_unlock(struct qspinlock *lock) + } + #endif + +-#define virt_queued_spin_lock virt_queued_spin_lock +- +-static inline bool virt_queued_spin_lock(struct qspinlock *lock) ++#ifdef CONFIG_PARAVIRT ++#define virt_spin_lock virt_spin_lock ++static inline bool virt_spin_lock(struct qspinlock *lock) + { + if (!static_cpu_has(X86_FEATURE_HYPERVISOR)) + return false; + +- while (atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL) != 0) +- cpu_relax(); ++ /* ++ * On hypervisors without PARAVIRT_SPINLOCKS support we fall ++ * back to a Test-and-Set spinlock, because fair locks have ++ * horrible lock 'holder' preemption issues. ++ */ ++ ++ do { ++ while (atomic_read(&lock->val) != 0) ++ cpu_relax(); ++ } while (atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL) != 0); + + return true; + } ++#endif /* CONFIG_PARAVIRT */ + + #include + +diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c +index c42827eb86cf..25f909362b7a 100644 +--- a/arch/x86/kernel/alternative.c ++++ b/arch/x86/kernel/alternative.c +@@ -338,10 +338,15 @@ done: + + static void __init_or_module optimize_nops(struct alt_instr *a, u8 *instr) + { ++ unsigned long flags; ++ + if (instr[0] != 0x90) + return; + ++ local_irq_save(flags); + add_nops(instr + (a->instrlen - a->padlen), a->padlen); ++ sync_core(); ++ local_irq_restore(flags); + + DUMP_BYTES(instr, a->instrlen, "%p: [%d:%d) optimized NOPs: ", + instr, a->instrlen - a->padlen, a->padlen); +diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c +index cde732c1b495..307a49828826 100644 +--- a/arch/x86/kernel/apic/apic.c ++++ b/arch/x86/kernel/apic/apic.c +@@ -336,6 +336,13 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) + apic_write(APIC_LVTT, lvtt_value); + + if (lvtt_value & APIC_LVT_TIMER_TSCDEADLINE) { ++ /* ++ * See Intel SDM: TSC-Deadline Mode chapter. In xAPIC mode, ++ * writing to the APIC LVTT and TSC_DEADLINE MSR isn't serialized. ++ * According to Intel, MFENCE can do the serialization here. ++ */ ++ asm volatile("mfence" : : : "memory"); ++ + printk_once(KERN_DEBUG "TSC deadline timer enabled\n"); + return; + } +diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c +index 206052e55517..5880b482d83c 100644 +--- a/arch/x86/kernel/apic/io_apic.c ++++ b/arch/x86/kernel/apic/io_apic.c +@@ -2522,6 +2522,7 @@ void __init setup_ioapic_dest(void) + int pin, ioapic, irq, irq_entry; + const struct cpumask *mask; + struct irq_data *idata; ++ struct irq_chip *chip; + + if (skip_ioapic_setup == 1) + return; +@@ -2545,9 +2546,9 @@ void __init setup_ioapic_dest(void) + else + mask = apic->target_cpus(); + +- irq_set_affinity(irq, mask); ++ chip = irq_data_get_irq_chip(idata); ++ chip->irq_set_affinity(idata, mask, false); + } +- + } + #endif + +diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c +index 6326ae24e4d5..1b09c420c7ff 100644 +--- a/arch/x86/kernel/cpu/perf_event_intel.c ++++ b/arch/x86/kernel/cpu/perf_event_intel.c +@@ -2102,9 +2102,12 @@ static struct event_constraint * + intel_get_event_constraints(struct cpu_hw_events *cpuc, int idx, + struct perf_event *event) + { +- struct event_constraint *c1 = cpuc->event_constraint[idx]; ++ struct event_constraint *c1 = NULL; + struct event_constraint *c2; + ++ if (idx >= 0) /* fake does < 0 */ ++ c1 = cpuc->event_constraint[idx]; ++ + /* + * first time only + * - static constraint: no change across incremental scheduling calls +diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c +index e068d6683dba..74ca2fe7a0b3 100644 +--- a/arch/x86/kernel/crash.c ++++ b/arch/x86/kernel/crash.c +@@ -185,10 +185,9 @@ void native_machine_crash_shutdown(struct pt_regs *regs) + } + + #ifdef CONFIG_KEXEC_FILE +-static int get_nr_ram_ranges_callback(unsigned long start_pfn, +- unsigned long nr_pfn, void *arg) ++static int get_nr_ram_ranges_callback(u64 start, u64 end, void *arg) + { +- int *nr_ranges = arg; ++ unsigned int *nr_ranges = arg; + + (*nr_ranges)++; + return 0; +@@ -214,7 +213,7 @@ static void fill_up_crash_elf_data(struct crash_elf_data *ced, + + ced->image = image; + +- walk_system_ram_range(0, -1, &nr_ranges, ++ walk_system_ram_res(0, -1, &nr_ranges, + get_nr_ram_ranges_callback); + + ced->max_nr_ranges = nr_ranges; +diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c +index 58bcfb67c01f..ebb5657ee280 100644 +--- a/arch/x86/kernel/paravirt.c ++++ b/arch/x86/kernel/paravirt.c +@@ -41,10 +41,18 @@ + #include + #include + +-/* nop stub */ +-void _paravirt_nop(void) +-{ +-} ++/* ++ * nop stub, which must not clobber anything *including the stack* to ++ * avoid confusing the entry prologues. ++ */ ++extern void _paravirt_nop(void); ++asm (".pushsection .entry.text, \"ax\"\n" ++ ".global _paravirt_nop\n" ++ "_paravirt_nop:\n\t" ++ "ret\n\t" ++ ".size _paravirt_nop, . - _paravirt_nop\n\t" ++ ".type _paravirt_nop, @function\n\t" ++ ".popsection"); + + /* identity function, which can be inlined */ + u32 _paravirt_ident_32(u32 x) +diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c +index f6b916387590..a90ac95562af 100644 +--- a/arch/x86/kernel/process_64.c ++++ b/arch/x86/kernel/process_64.c +@@ -497,27 +497,59 @@ void set_personality_ia32(bool x32) + } + EXPORT_SYMBOL_GPL(set_personality_ia32); + ++/* ++ * Called from fs/proc with a reference on @p to find the function ++ * which called into schedule(). This needs to be done carefully ++ * because the task might wake up and we might look at a stack ++ * changing under us. ++ */ + unsigned long get_wchan(struct task_struct *p) + { +- unsigned long stack; +- u64 fp, ip; ++ unsigned long start, bottom, top, sp, fp, ip; + int count = 0; + + if (!p || p == current || p->state == TASK_RUNNING) + return 0; +- stack = (unsigned long)task_stack_page(p); +- if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE) ++ ++ start = (unsigned long)task_stack_page(p); ++ if (!start) ++ return 0; ++ ++ /* ++ * Layout of the stack page: ++ * ++ * ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long) ++ * PADDING ++ * ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING ++ * stack ++ * ----------- bottom = start + sizeof(thread_info) ++ * thread_info ++ * ----------- start ++ * ++ * The tasks stack pointer points at the location where the ++ * framepointer is stored. The data on the stack is: ++ * ... IP FP ... IP FP ++ * ++ * We need to read FP and IP, so we need to adjust the upper ++ * bound by another unsigned long. ++ */ ++ top = start + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; ++ top -= 2 * sizeof(unsigned long); ++ bottom = start + sizeof(struct thread_info); ++ ++ sp = READ_ONCE(p->thread.sp); ++ if (sp < bottom || sp > top) + return 0; +- fp = *(u64 *)(p->thread.sp); ++ ++ fp = READ_ONCE(*(unsigned long *)sp); + do { +- if (fp < (unsigned long)stack || +- fp >= (unsigned long)stack+THREAD_SIZE) ++ if (fp < bottom || fp > top) + return 0; +- ip = *(u64 *)(fp+8); ++ ip = READ_ONCE(*(unsigned long *)(fp + sizeof(unsigned long))); + if (!in_sched_functions(ip)) + return ip; +- fp = *(u64 *)fp; +- } while (count++ < 16); ++ fp = READ_ONCE(*(unsigned long *)fp); ++ } while (count++ < 16 && p->state != TASK_RUNNING); + return 0; + } + +diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c +index 7437b41f6a47..dc9af7a0839a 100644 +--- a/arch/x86/kernel/tsc.c ++++ b/arch/x86/kernel/tsc.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */ + EXPORT_SYMBOL(cpu_khz); +@@ -1013,15 +1014,17 @@ EXPORT_SYMBOL_GPL(mark_tsc_unstable); + + static void __init check_system_tsc_reliable(void) + { +-#ifdef CONFIG_MGEODE_LX +- /* RTSC counts during suspend */ ++#if defined(CONFIG_MGEODEGX1) || defined(CONFIG_MGEODE_LX) || defined(CONFIG_X86_GENERIC) ++ if (is_geode_lx()) { ++ /* RTSC counts during suspend */ + #define RTSC_SUSP 0x100 +- unsigned long res_low, res_high; ++ unsigned long res_low, res_high; + +- rdmsr_safe(MSR_GEODE_BUSCONT_CONF0, &res_low, &res_high); +- /* Geode_LX - the OLPC CPU has a very reliable TSC */ +- if (res_low & RTSC_SUSP) +- tsc_clocksource_reliable = 1; ++ rdmsr_safe(MSR_GEODE_BUSCONT_CONF0, &res_low, &res_high); ++ /* Geode_LX - the OLPC CPU has a very reliable TSC */ ++ if (res_low & RTSC_SUSP) ++ tsc_clocksource_reliable = 1; ++ } + #endif + if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) + tsc_clocksource_reliable = 1; +diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c +index 8e0c0844c6b9..2d32b67a1043 100644 +--- a/arch/x86/kvm/svm.c ++++ b/arch/x86/kvm/svm.c +@@ -513,7 +513,7 @@ static void skip_emulated_instruction(struct kvm_vcpu *vcpu) + struct vcpu_svm *svm = to_svm(vcpu); + + if (svm->vmcb->control.next_rip != 0) { +- WARN_ON(!static_cpu_has(X86_FEATURE_NRIPS)); ++ WARN_ON_ONCE(!static_cpu_has(X86_FEATURE_NRIPS)); + svm->next_rip = svm->vmcb->control.next_rip; + } + +@@ -865,64 +865,6 @@ static void svm_disable_lbrv(struct vcpu_svm *svm) + set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 0, 0); + } + +-#define MTRR_TYPE_UC_MINUS 7 +-#define MTRR2PROTVAL_INVALID 0xff +- +-static u8 mtrr2protval[8]; +- +-static u8 fallback_mtrr_type(int mtrr) +-{ +- /* +- * WT and WP aren't always available in the host PAT. Treat +- * them as UC and UC- respectively. Everything else should be +- * there. +- */ +- switch (mtrr) +- { +- case MTRR_TYPE_WRTHROUGH: +- return MTRR_TYPE_UNCACHABLE; +- case MTRR_TYPE_WRPROT: +- return MTRR_TYPE_UC_MINUS; +- default: +- BUG(); +- } +-} +- +-static void build_mtrr2protval(void) +-{ +- int i; +- u64 pat; +- +- for (i = 0; i < 8; i++) +- mtrr2protval[i] = MTRR2PROTVAL_INVALID; +- +- /* Ignore the invalid MTRR types. */ +- mtrr2protval[2] = 0; +- mtrr2protval[3] = 0; +- +- /* +- * Use host PAT value to figure out the mapping from guest MTRR +- * values to nested page table PAT/PCD/PWT values. We do not +- * want to change the host PAT value every time we enter the +- * guest. +- */ +- rdmsrl(MSR_IA32_CR_PAT, pat); +- for (i = 0; i < 8; i++) { +- u8 mtrr = pat >> (8 * i); +- +- if (mtrr2protval[mtrr] == MTRR2PROTVAL_INVALID) +- mtrr2protval[mtrr] = __cm_idx2pte(i); +- } +- +- for (i = 0; i < 8; i++) { +- if (mtrr2protval[i] == MTRR2PROTVAL_INVALID) { +- u8 fallback = fallback_mtrr_type(i); +- mtrr2protval[i] = mtrr2protval[fallback]; +- BUG_ON(mtrr2protval[i] == MTRR2PROTVAL_INVALID); +- } +- } +-} +- + static __init int svm_hardware_setup(void) + { + int cpu; +@@ -989,7 +931,6 @@ static __init int svm_hardware_setup(void) + } else + kvm_disable_tdp(); + +- build_mtrr2protval(); + return 0; + + err: +@@ -1144,39 +1085,6 @@ static u64 svm_compute_tsc_offset(struct kvm_vcpu *vcpu, u64 target_tsc) + return target_tsc - tsc; + } + +-static void svm_set_guest_pat(struct vcpu_svm *svm, u64 *g_pat) +-{ +- struct kvm_vcpu *vcpu = &svm->vcpu; +- +- /* Unlike Intel, AMD takes the guest's CR0.CD into account. +- * +- * AMD does not have IPAT. To emulate it for the case of guests +- * with no assigned devices, just set everything to WB. If guests +- * have assigned devices, however, we cannot force WB for RAM +- * pages only, so use the guest PAT directly. +- */ +- if (!kvm_arch_has_assigned_device(vcpu->kvm)) +- *g_pat = 0x0606060606060606; +- else +- *g_pat = vcpu->arch.pat; +-} +- +-static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) +-{ +- u8 mtrr; +- +- /* +- * 1. MMIO: trust guest MTRR, so same as item 3. +- * 2. No passthrough: always map as WB, and force guest PAT to WB as well +- * 3. Passthrough: can't guarantee the result, try to trust guest. +- */ +- if (!is_mmio && !kvm_arch_has_assigned_device(vcpu->kvm)) +- return 0; +- +- mtrr = kvm_mtrr_get_guest_memory_type(vcpu, gfn); +- return mtrr2protval[mtrr]; +-} +- + static void init_vmcb(struct vcpu_svm *svm, bool init_event) + { + struct vmcb_control_area *control = &svm->vmcb->control; +@@ -1260,6 +1168,7 @@ static void init_vmcb(struct vcpu_svm *svm, bool init_event) + * It also updates the guest-visible cr0 value. + */ + (void)kvm_set_cr0(&svm->vcpu, X86_CR0_NW | X86_CR0_CD | X86_CR0_ET); ++ kvm_mmu_reset_context(&svm->vcpu); + + save->cr4 = X86_CR4_PAE; + /* rdx = ?? */ +@@ -1272,7 +1181,6 @@ static void init_vmcb(struct vcpu_svm *svm, bool init_event) + clr_cr_intercept(svm, INTERCEPT_CR3_READ); + clr_cr_intercept(svm, INTERCEPT_CR3_WRITE); + save->g_pat = svm->vcpu.arch.pat; +- svm_set_guest_pat(svm, &save->g_pat); + save->cr3 = 0; + save->cr4 = 0; + } +@@ -3347,16 +3255,6 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) + case MSR_VM_IGNNE: + vcpu_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data); + break; +- case MSR_IA32_CR_PAT: +- if (npt_enabled) { +- if (!kvm_mtrr_valid(vcpu, MSR_IA32_CR_PAT, data)) +- return 1; +- vcpu->arch.pat = data; +- svm_set_guest_pat(svm, &svm->vmcb->save.g_pat); +- mark_dirty(svm->vmcb, VMCB_NPT); +- break; +- } +- /* fall through */ + default: + return kvm_set_msr_common(vcpu, msr); + } +@@ -4191,6 +4089,11 @@ static bool svm_has_high_real_mode_segbase(void) + return true; + } + ++static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) ++{ ++ return 0; ++} ++ + static void svm_cpuid_update(struct kvm_vcpu *vcpu) + { + } +diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c +index 83b7b5cd75d5..aa9e8229571d 100644 +--- a/arch/x86/kvm/vmx.c ++++ b/arch/x86/kvm/vmx.c +@@ -6134,6 +6134,8 @@ static __init int hardware_setup(void) + memcpy(vmx_msr_bitmap_longmode_x2apic, + vmx_msr_bitmap_longmode, PAGE_SIZE); + ++ set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */ ++ + if (enable_apicv) { + for (msr = 0x800; msr <= 0x8ff; msr++) + vmx_disable_intercept_msr_read_x2apic(msr); +@@ -8632,17 +8634,22 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) + u64 ipat = 0; + + /* For VT-d and EPT combination +- * 1. MMIO: guest may want to apply WC, trust it. ++ * 1. MMIO: always map as UC + * 2. EPT with VT-d: + * a. VT-d without snooping control feature: can't guarantee the +- * result, try to trust guest. So the same as item 1. ++ * result, try to trust guest. + * b. VT-d with snooping control feature: snooping control feature of + * VT-d engine can guarantee the cache correctness. Just set it + * to WB to keep consistent with host. So the same as item 3. + * 3. EPT without VT-d: always map as WB and set IPAT=1 to keep + * consistent with host MTRR + */ +- if (!is_mmio && !kvm_arch_has_noncoherent_dma(vcpu->kvm)) { ++ if (is_mmio) { ++ cache = MTRR_TYPE_UNCACHABLE; ++ goto exit; ++ } ++ ++ if (!kvm_arch_has_noncoherent_dma(vcpu->kvm)) { + ipat = VMX_EPT_IPAT_BIT; + cache = MTRR_TYPE_WRBACK; + goto exit; +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 8f0f6eca69da..32c6e6ac5964 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -2388,6 +2388,8 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + case MSR_IA32_LASTINTFROMIP: + case MSR_IA32_LASTINTTOIP: + case MSR_K8_SYSCFG: ++ case MSR_K8_TSEG_ADDR: ++ case MSR_K8_TSEG_MASK: + case MSR_K7_HWCR: + case MSR_VM_HSAVE_PA: + case MSR_K8_INT_PENDING_MSG: +diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c +index 3fba623e3ba5..f9977a7a9444 100644 +--- a/arch/x86/mm/init_64.c ++++ b/arch/x86/mm/init_64.c +@@ -1132,7 +1132,7 @@ void mark_rodata_ro(void) + * has been zapped already via cleanup_highmem(). + */ + all_end = roundup((unsigned long)_brk_end, PMD_SIZE); +- set_memory_nx(rodata_start, (all_end - rodata_start) >> PAGE_SHIFT); ++ set_memory_nx(text_end, (all_end - text_end) >> PAGE_SHIFT); + + rodata_test(); + +diff --git a/arch/x86/pci/intel_mid_pci.c b/arch/x86/pci/intel_mid_pci.c +index 27062303c881..7553921c146c 100644 +--- a/arch/x86/pci/intel_mid_pci.c ++++ b/arch/x86/pci/intel_mid_pci.c +@@ -35,6 +35,9 @@ + + #define PCIE_CAP_OFFSET 0x100 + ++/* Quirks for the listed devices */ ++#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 ++ + /* Fixed BAR fields */ + #define PCIE_VNDR_CAP_ID_FIXED_BAR 0x00 /* Fixed BAR (TBD) */ + #define PCI_FIXED_BAR_0_SIZE 0x04 +@@ -214,10 +217,27 @@ static int intel_mid_pci_irq_enable(struct pci_dev *dev) + if (dev->irq_managed && dev->irq > 0) + return 0; + +- if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER) ++ switch (intel_mid_identify_cpu()) { ++ case INTEL_MID_CPU_CHIP_TANGIER: + polarity = 0; /* active high */ +- else ++ ++ /* Special treatment for IRQ0 */ ++ if (dev->irq == 0) { ++ /* ++ * TNG has IRQ0 assigned to eMMC controller. But there ++ * are also other devices with bogus PCI configuration ++ * that have IRQ0 assigned. This check ensures that ++ * eMMC gets it. ++ */ ++ if (dev->device != PCI_DEVICE_ID_INTEL_MRFL_MMC) ++ return -EBUSY; ++ } ++ break; ++ default: + polarity = 1; /* active low */ ++ break; ++ } ++ + ioapic_set_alloc_attr(&info, dev_to_node(&dev->dev), 1, polarity); + + /* +diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c +index e4308fe6afe8..c6835bfad3a1 100644 +--- a/arch/x86/platform/efi/efi.c ++++ b/arch/x86/platform/efi/efi.c +@@ -705,6 +705,70 @@ out: + } + + /* ++ * Iterate the EFI memory map in reverse order because the regions ++ * will be mapped top-down. The end result is the same as if we had ++ * mapped things forward, but doesn't require us to change the ++ * existing implementation of efi_map_region(). ++ */ ++static inline void *efi_map_next_entry_reverse(void *entry) ++{ ++ /* Initial call */ ++ if (!entry) ++ return memmap.map_end - memmap.desc_size; ++ ++ entry -= memmap.desc_size; ++ if (entry < memmap.map) ++ return NULL; ++ ++ return entry; ++} ++ ++/* ++ * efi_map_next_entry - Return the next EFI memory map descriptor ++ * @entry: Previous EFI memory map descriptor ++ * ++ * This is a helper function to iterate over the EFI memory map, which ++ * we do in different orders depending on the current configuration. ++ * ++ * To begin traversing the memory map @entry must be %NULL. ++ * ++ * Returns %NULL when we reach the end of the memory map. ++ */ ++static void *efi_map_next_entry(void *entry) ++{ ++ if (!efi_enabled(EFI_OLD_MEMMAP) && efi_enabled(EFI_64BIT)) { ++ /* ++ * Starting in UEFI v2.5 the EFI_PROPERTIES_TABLE ++ * config table feature requires us to map all entries ++ * in the same order as they appear in the EFI memory ++ * map. That is to say, entry N must have a lower ++ * virtual address than entry N+1. This is because the ++ * firmware toolchain leaves relative references in ++ * the code/data sections, which are split and become ++ * separate EFI memory regions. Mapping things ++ * out-of-order leads to the firmware accessing ++ * unmapped addresses. ++ * ++ * Since we need to map things this way whether or not ++ * the kernel actually makes use of ++ * EFI_PROPERTIES_TABLE, let's just switch to this ++ * scheme by default for 64-bit. ++ */ ++ return efi_map_next_entry_reverse(entry); ++ } ++ ++ /* Initial call */ ++ if (!entry) ++ return memmap.map; ++ ++ entry += memmap.desc_size; ++ if (entry >= memmap.map_end) ++ return NULL; ++ ++ return entry; ++} ++ ++/* + * Map the efi memory ranges of the runtime services and update new_mmap with + * virtual addresses. + */ +@@ -714,7 +778,8 @@ static void * __init efi_map_regions(int *count, int *pg_shift) + unsigned long left = 0; + efi_memory_desc_t *md; + +- for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { ++ p = NULL; ++ while ((p = efi_map_next_entry(p))) { + md = p; + if (!(md->attribute & EFI_MEMORY_RUNTIME)) { + #ifdef CONFIG_X86_64 +diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c +index 11d6fb4e8483..777ad2f03160 100644 +--- a/arch/x86/xen/enlighten.c ++++ b/arch/x86/xen/enlighten.c +@@ -33,6 +33,10 @@ + #include + #include + ++#ifdef CONFIG_KEXEC_CORE ++#include ++#endif ++ + #include + #include + #include +@@ -1800,6 +1804,21 @@ static struct notifier_block xen_hvm_cpu_notifier = { + .notifier_call = xen_hvm_cpu_notify, + }; + ++#ifdef CONFIG_KEXEC_CORE ++static void xen_hvm_shutdown(void) ++{ ++ native_machine_shutdown(); ++ if (kexec_in_progress) ++ xen_reboot(SHUTDOWN_soft_reset); ++} ++ ++static void xen_hvm_crash_shutdown(struct pt_regs *regs) ++{ ++ native_machine_crash_shutdown(regs); ++ xen_reboot(SHUTDOWN_soft_reset); ++} ++#endif ++ + static void __init xen_hvm_guest_init(void) + { + if (xen_pv_domain()) +@@ -1819,6 +1838,10 @@ static void __init xen_hvm_guest_init(void) + x86_init.irqs.intr_init = xen_init_IRQ; + xen_hvm_init_time_ops(); + xen_hvm_init_mmu_ops(); ++#ifdef CONFIG_KEXEC_CORE ++ machine_ops.shutdown = xen_hvm_shutdown; ++ machine_ops.crash_shutdown = xen_hvm_crash_shutdown; ++#endif + } + #endif + +diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c +index d6283b3f5db5..9cc48d1d7abb 100644 +--- a/block/blk-cgroup.c ++++ b/block/blk-cgroup.c +@@ -387,6 +387,9 @@ static void blkg_destroy_all(struct request_queue *q) + blkg_destroy(blkg); + spin_unlock(&blkcg->lock); + } ++ ++ q->root_blkg = NULL; ++ q->root_rl.blkg = NULL; + } + + /* +diff --git a/block/blk-mq.c b/block/blk-mq.c +index 176262ec3731..c69902695136 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -1807,7 +1807,6 @@ static void blk_mq_map_swqueue(struct request_queue *q) + + hctx = q->mq_ops->map_queue(q, i); + cpumask_set_cpu(i, hctx->cpumask); +- cpumask_set_cpu(i, hctx->tags->cpumask); + ctx->index_hw = hctx->nr_ctx; + hctx->ctxs[hctx->nr_ctx++] = ctx; + } +@@ -1847,6 +1846,14 @@ static void blk_mq_map_swqueue(struct request_queue *q) + hctx->next_cpu = cpumask_first(hctx->cpumask); + hctx->next_cpu_batch = BLK_MQ_CPU_WORK_BATCH; + } ++ ++ queue_for_each_ctx(q, ctx, i) { ++ if (!cpu_online(i)) ++ continue; ++ ++ hctx = q->mq_ops->map_queue(q, i); ++ cpumask_set_cpu(i, hctx->tags->cpumask); ++ } + } + + static void blk_mq_update_tag_set_depth(struct blk_mq_tag_set *set) +diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c +index 764280a91776..e9fd32e91668 100644 +--- a/drivers/base/cacheinfo.c ++++ b/drivers/base/cacheinfo.c +@@ -148,7 +148,11 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) + + if (sibling == cpu) /* skip itself */ + continue; ++ + sib_cpu_ci = get_cpu_cacheinfo(sibling); ++ if (!sib_cpu_ci->info_list) ++ continue; ++ + sib_leaf = sib_cpu_ci->info_list + index; + cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); + cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); +@@ -159,6 +163,9 @@ static void cache_shared_cpu_map_remove(unsigned int cpu) + + static void free_cache_attributes(unsigned int cpu) + { ++ if (!per_cpu_cacheinfo(cpu)) ++ return; ++ + cache_shared_cpu_map_remove(cpu); + + kfree(per_cpu_cacheinfo(cpu)); +@@ -514,8 +521,7 @@ static int cacheinfo_cpu_callback(struct notifier_block *nfb, + break; + case CPU_DEAD: + cache_remove_dev(cpu); +- if (per_cpu_cacheinfo(cpu)) +- free_cache_attributes(cpu); ++ free_cache_attributes(cpu); + break; + } + return notifier_from_errno(rc); +diff --git a/drivers/base/property.c b/drivers/base/property.c +index f3f6d167f3f1..37a7bb7b239d 100644 +--- a/drivers/base/property.c ++++ b/drivers/base/property.c +@@ -27,9 +27,10 @@ + */ + void device_add_property_set(struct device *dev, struct property_set *pset) + { +- if (pset) +- pset->fwnode.type = FWNODE_PDATA; ++ if (!pset) ++ return; + ++ pset->fwnode.type = FWNODE_PDATA; + set_secondary_fwnode(dev, &pset->fwnode); + } + EXPORT_SYMBOL_GPL(device_add_property_set); +diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c +index 5799a0b9e6cc..c8941f39c919 100644 +--- a/drivers/base/regmap/regmap-debugfs.c ++++ b/drivers/base/regmap/regmap-debugfs.c +@@ -32,8 +32,7 @@ static DEFINE_MUTEX(regmap_debugfs_early_lock); + /* Calculate the length of a fixed format */ + static size_t regmap_calc_reg_len(int max_val, char *buf, size_t buf_size) + { +- snprintf(buf, buf_size, "%x", max_val); +- return strlen(buf); ++ return snprintf(NULL, 0, "%x", max_val); + } + + static ssize_t regmap_name_read_file(struct file *file, +@@ -432,7 +431,7 @@ static ssize_t regmap_access_read_file(struct file *file, + /* If we're in the region the user is trying to read */ + if (p >= *ppos) { + /* ...but not beyond it */ +- if (buf_pos >= count - 1 - tot_len) ++ if (buf_pos + tot_len + 1 >= count) + break; + + /* Format the register */ +diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c +index deb3f001791f..767657565de6 100644 +--- a/drivers/block/xen-blkback/xenbus.c ++++ b/drivers/block/xen-blkback/xenbus.c +@@ -212,6 +212,9 @@ static int xen_blkif_map(struct xen_blkif *blkif, grant_ref_t *gref, + + static int xen_blkif_disconnect(struct xen_blkif *blkif) + { ++ struct pending_req *req, *n; ++ int i = 0, j; ++ + if (blkif->xenblkd) { + kthread_stop(blkif->xenblkd); + wake_up(&blkif->shutdown_wq); +@@ -238,13 +241,28 @@ static int xen_blkif_disconnect(struct xen_blkif *blkif) + /* Remove all persistent grants and the cache of ballooned pages. */ + xen_blkbk_free_caches(blkif); + ++ /* Check that there is no request in use */ ++ list_for_each_entry_safe(req, n, &blkif->pending_free, free_list) { ++ list_del(&req->free_list); ++ ++ for (j = 0; j < MAX_INDIRECT_SEGMENTS; j++) ++ kfree(req->segments[j]); ++ ++ for (j = 0; j < MAX_INDIRECT_PAGES; j++) ++ kfree(req->indirect_pages[j]); ++ ++ kfree(req); ++ i++; ++ } ++ ++ WARN_ON(i != (XEN_BLKIF_REQS_PER_PAGE * blkif->nr_ring_pages)); ++ blkif->nr_ring_pages = 0; ++ + return 0; + } + + static void xen_blkif_free(struct xen_blkif *blkif) + { +- struct pending_req *req, *n; +- int i = 0, j; + + xen_blkif_disconnect(blkif); + xen_vbd_free(&blkif->vbd); +@@ -257,22 +275,6 @@ static void xen_blkif_free(struct xen_blkif *blkif) + BUG_ON(!list_empty(&blkif->free_pages)); + BUG_ON(!RB_EMPTY_ROOT(&blkif->persistent_gnts)); + +- /* Check that there is no request in use */ +- list_for_each_entry_safe(req, n, &blkif->pending_free, free_list) { +- list_del(&req->free_list); +- +- for (j = 0; j < MAX_INDIRECT_SEGMENTS; j++) +- kfree(req->segments[j]); +- +- for (j = 0; j < MAX_INDIRECT_PAGES; j++) +- kfree(req->indirect_pages[j]); +- +- kfree(req); +- i++; +- } +- +- WARN_ON(i != (XEN_BLKIF_REQS_PER_PAGE * blkif->nr_ring_pages)); +- + kmem_cache_free(xen_blkif_cachep, blkif); + } + +diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c +index 3a1fe07cfe9e..dd02356e2e86 100644 +--- a/drivers/clk/samsung/clk-cpu.c ++++ b/drivers/clk/samsung/clk-cpu.c +@@ -161,7 +161,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, + * the values for DIV_COPY and DIV_HPM dividers need not be set. + */ + div0 = cfg_data->div0; +- if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { ++ if (cpuclk->flags & CLK_CPU_HAS_DIV1) { + div1 = cfg_data->div1; + if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK) + div1 = readl(base + E4210_DIV_CPU1) & +@@ -182,7 +182,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, + alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; + WARN_ON(alt_div >= MAX_DIV); + +- if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { ++ if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { + /* + * In Exynos4210, ATB clock parent is also mout_core. So + * ATB clock also needs to be mantained at safe speed. +@@ -203,7 +203,7 @@ static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, + writel(div0, base + E4210_DIV_CPU0); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL); + +- if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { ++ if (cpuclk->flags & CLK_CPU_HAS_DIV1) { + writel(div1, base + E4210_DIV_CPU1); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU1, + DIV_MASK_ALL); +@@ -222,7 +222,7 @@ static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, + unsigned long mux_reg; + + /* find out the divider values to use for clock data */ +- if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { ++ if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; +@@ -237,7 +237,7 @@ static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, + writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU); + wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1); + +- if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { ++ if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) { + div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); + div_mask |= E4210_DIV0_ATB_MASK; + } +diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c +index 757636d166cf..4ab28cfb8d2a 100644 +--- a/drivers/clk/ti/clk-3xxx.c ++++ b/drivers/clk/ti/clk-3xxx.c +@@ -163,7 +163,6 @@ static struct ti_dt_clk omap3xxx_clks[] = { + DT_CLK(NULL, "gpio2_ick", "gpio2_ick"), + DT_CLK(NULL, "wdt3_ick", "wdt3_ick"), + DT_CLK(NULL, "uart3_ick", "uart3_ick"), +- DT_CLK(NULL, "uart4_ick", "uart4_ick"), + DT_CLK(NULL, "gpt9_ick", "gpt9_ick"), + DT_CLK(NULL, "gpt8_ick", "gpt8_ick"), + DT_CLK(NULL, "gpt7_ick", "gpt7_ick"), +@@ -308,6 +307,7 @@ static struct ti_dt_clk am35xx_clks[] = { + static struct ti_dt_clk omap36xx_clks[] = { + DT_CLK(NULL, "omap_192m_alwon_fck", "omap_192m_alwon_fck"), + DT_CLK(NULL, "uart4_fck", "uart4_fck"), ++ DT_CLK(NULL, "uart4_ick", "uart4_ick"), + { .node_name = NULL }, + }; + +diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c +index 63b8323df918..0eb82107c421 100644 +--- a/drivers/clk/ti/clk-7xx.c ++++ b/drivers/clk/ti/clk-7xx.c +@@ -16,7 +16,6 @@ + #include + #include + +-#define DRA7_DPLL_ABE_DEFFREQ 180633600 + #define DRA7_DPLL_GMAC_DEFFREQ 1000000000 + #define DRA7_DPLL_USB_DEFFREQ 960000000 + +@@ -312,27 +311,12 @@ static struct ti_dt_clk dra7xx_clks[] = { + int __init dra7xx_dt_clk_init(void) + { + int rc; +- struct clk *abe_dpll_mux, *sys_clkin2, *dpll_ck, *hdcp_ck; ++ struct clk *dpll_ck, *hdcp_ck; + + ti_dt_clocks_register(dra7xx_clks); + + omap2_clk_disable_autoidle_all(); + +- abe_dpll_mux = clk_get_sys(NULL, "abe_dpll_sys_clk_mux"); +- sys_clkin2 = clk_get_sys(NULL, "sys_clkin2"); +- dpll_ck = clk_get_sys(NULL, "dpll_abe_ck"); +- +- rc = clk_set_parent(abe_dpll_mux, sys_clkin2); +- if (!rc) +- rc = clk_set_rate(dpll_ck, DRA7_DPLL_ABE_DEFFREQ); +- if (rc) +- pr_err("%s: failed to configure ABE DPLL!\n", __func__); +- +- dpll_ck = clk_get_sys(NULL, "dpll_abe_m2x2_ck"); +- rc = clk_set_rate(dpll_ck, DRA7_DPLL_ABE_DEFFREQ * 2); +- if (rc) +- pr_err("%s: failed to configure ABE DPLL m2x2!\n", __func__); +- + dpll_ck = clk_get_sys(NULL, "dpll_gmac_ck"); + rc = clk_set_rate(dpll_ck, DRA7_DPLL_GMAC_DEFFREQ); + if (rc) +diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c +index 0136dfcdabf0..7c2a7385c2ad 100644 +--- a/drivers/cpufreq/acpi-cpufreq.c ++++ b/drivers/cpufreq/acpi-cpufreq.c +@@ -146,6 +146,9 @@ static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) + { + struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); + ++ if (unlikely(!data)) ++ return -ENODEV; ++ + return cpufreq_show_cpus(data->freqdomain_cpus, buf); + } + +diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c +index 528a82bf5038..99a406501e8c 100644 +--- a/drivers/cpufreq/cpufreq-dt.c ++++ b/drivers/cpufreq/cpufreq-dt.c +@@ -255,7 +255,8 @@ static int cpufreq_init(struct cpufreq_policy *policy) + rcu_read_unlock(); + + tol_uV = opp_uV * priv->voltage_tolerance / 100; +- if (regulator_is_supported_voltage(cpu_reg, opp_uV, ++ if (regulator_is_supported_voltage(cpu_reg, ++ opp_uV - tol_uV, + opp_uV + tol_uV)) { + if (opp_uV < min_uV) + min_uV = opp_uV; +diff --git a/drivers/crypto/marvell/cesa.h b/drivers/crypto/marvell/cesa.h +index b60698b30d30..bc2a55bc35e4 100644 +--- a/drivers/crypto/marvell/cesa.h ++++ b/drivers/crypto/marvell/cesa.h +@@ -687,6 +687,33 @@ static inline u32 mv_cesa_get_int_mask(struct mv_cesa_engine *engine) + + int mv_cesa_queue_req(struct crypto_async_request *req); + ++/* ++ * Helper function that indicates whether a crypto request needs to be ++ * cleaned up or not after being enqueued using mv_cesa_queue_req(). ++ */ ++static inline int mv_cesa_req_needs_cleanup(struct crypto_async_request *req, ++ int ret) ++{ ++ /* ++ * The queue still had some space, the request was queued ++ * normally, so there's no need to clean it up. ++ */ ++ if (ret == -EINPROGRESS) ++ return false; ++ ++ /* ++ * The queue had not space left, but since the request is ++ * flagged with CRYPTO_TFM_REQ_MAY_BACKLOG, it was added to ++ * the backlog and will be processed later. There's no need to ++ * clean it up. ++ */ ++ if (ret == -EBUSY && req->flags & CRYPTO_TFM_REQ_MAY_BACKLOG) ++ return false; ++ ++ /* Request wasn't queued, we need to clean it up */ ++ return true; ++} ++ + /* TDMA functions */ + + static inline void mv_cesa_req_dma_iter_init(struct mv_cesa_dma_iter *iter, +diff --git a/drivers/crypto/marvell/cipher.c b/drivers/crypto/marvell/cipher.c +index 0745cf3b9c0e..3df2f4e7adb2 100644 +--- a/drivers/crypto/marvell/cipher.c ++++ b/drivers/crypto/marvell/cipher.c +@@ -189,7 +189,6 @@ static inline void mv_cesa_ablkcipher_prepare(struct crypto_async_request *req, + { + struct ablkcipher_request *ablkreq = ablkcipher_request_cast(req); + struct mv_cesa_ablkcipher_req *creq = ablkcipher_request_ctx(ablkreq); +- + creq->req.base.engine = engine; + + if (creq->req.base.type == CESA_DMA_REQ) +@@ -431,7 +430,7 @@ static int mv_cesa_des_op(struct ablkcipher_request *req, + return ret; + + ret = mv_cesa_queue_req(&req->base); +- if (ret && ret != -EINPROGRESS) ++ if (mv_cesa_req_needs_cleanup(&req->base, ret)) + mv_cesa_ablkcipher_cleanup(req); + + return ret; +@@ -551,7 +550,7 @@ static int mv_cesa_des3_op(struct ablkcipher_request *req, + return ret; + + ret = mv_cesa_queue_req(&req->base); +- if (ret && ret != -EINPROGRESS) ++ if (mv_cesa_req_needs_cleanup(&req->base, ret)) + mv_cesa_ablkcipher_cleanup(req); + + return ret; +@@ -693,7 +692,7 @@ static int mv_cesa_aes_op(struct ablkcipher_request *req, + return ret; + + ret = mv_cesa_queue_req(&req->base); +- if (ret && ret != -EINPROGRESS) ++ if (mv_cesa_req_needs_cleanup(&req->base, ret)) + mv_cesa_ablkcipher_cleanup(req); + + return ret; +diff --git a/drivers/crypto/marvell/hash.c b/drivers/crypto/marvell/hash.c +index ae9272eb9c1a..e8d0d7128137 100644 +--- a/drivers/crypto/marvell/hash.c ++++ b/drivers/crypto/marvell/hash.c +@@ -739,10 +739,8 @@ static int mv_cesa_ahash_update(struct ahash_request *req) + return 0; + + ret = mv_cesa_queue_req(&req->base); +- if (ret && ret != -EINPROGRESS) { ++ if (mv_cesa_req_needs_cleanup(&req->base, ret)) + mv_cesa_ahash_cleanup(req); +- return ret; +- } + + return ret; + } +@@ -766,7 +764,7 @@ static int mv_cesa_ahash_final(struct ahash_request *req) + return 0; + + ret = mv_cesa_queue_req(&req->base); +- if (ret && ret != -EINPROGRESS) ++ if (mv_cesa_req_needs_cleanup(&req->base, ret)) + mv_cesa_ahash_cleanup(req); + + return ret; +@@ -791,7 +789,7 @@ static int mv_cesa_ahash_finup(struct ahash_request *req) + return 0; + + ret = mv_cesa_queue_req(&req->base); +- if (ret && ret != -EINPROGRESS) ++ if (mv_cesa_req_needs_cleanup(&req->base, ret)) + mv_cesa_ahash_cleanup(req); + + return ret; +diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c +index 40afa2a16cfc..da7917a2eed2 100644 +--- a/drivers/dma/at_xdmac.c ++++ b/drivers/dma/at_xdmac.c +@@ -455,6 +455,15 @@ static struct at_xdmac_desc *at_xdmac_alloc_desc(struct dma_chan *chan, + return desc; + } + ++void at_xdmac_init_used_desc(struct at_xdmac_desc *desc) ++{ ++ memset(&desc->lld, 0, sizeof(desc->lld)); ++ INIT_LIST_HEAD(&desc->descs_list); ++ desc->direction = DMA_TRANS_NONE; ++ desc->xfer_size = 0; ++ desc->active_xfer = false; ++} ++ + /* Call must be protected by lock. */ + static struct at_xdmac_desc *at_xdmac_get_desc(struct at_xdmac_chan *atchan) + { +@@ -466,7 +475,7 @@ static struct at_xdmac_desc *at_xdmac_get_desc(struct at_xdmac_chan *atchan) + desc = list_first_entry(&atchan->free_descs_list, + struct at_xdmac_desc, desc_node); + list_del(&desc->desc_node); +- desc->active_xfer = false; ++ at_xdmac_init_used_desc(desc); + } + + return desc; +@@ -797,10 +806,7 @@ at_xdmac_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, + list_add_tail(&desc->desc_node, &first->descs_list); + } + +- prev->lld.mbr_nda = first->tx_dma_desc.phys; +- dev_dbg(chan2dev(chan), +- "%s: chain lld: prev=0x%p, mbr_nda=%pad\n", +- __func__, prev, &prev->lld.mbr_nda); ++ at_xdmac_queue_desc(chan, prev, first); + first->tx_dma_desc.flags = flags; + first->xfer_size = buf_len; + first->direction = direction; +@@ -878,14 +884,14 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan, + + if (xt->src_inc) { + if (xt->src_sgl) +- chan_cc |= AT_XDMAC_CC_SAM_UBS_DS_AM; ++ chan_cc |= AT_XDMAC_CC_SAM_UBS_AM; + else + chan_cc |= AT_XDMAC_CC_SAM_INCREMENTED_AM; + } + + if (xt->dst_inc) { + if (xt->dst_sgl) +- chan_cc |= AT_XDMAC_CC_DAM_UBS_DS_AM; ++ chan_cc |= AT_XDMAC_CC_DAM_UBS_AM; + else + chan_cc |= AT_XDMAC_CC_DAM_INCREMENTED_AM; + } +diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c +index cf1c87fa1edd..bedce038c6e2 100644 +--- a/drivers/dma/dw/core.c ++++ b/drivers/dma/dw/core.c +@@ -1591,7 +1591,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) + INIT_LIST_HEAD(&dw->dma.channels); + for (i = 0; i < nr_channels; i++) { + struct dw_dma_chan *dwc = &dw->chan[i]; +- int r = nr_channels - i - 1; + + dwc->chan.device = &dw->dma; + dma_cookie_init(&dwc->chan); +@@ -1603,7 +1602,7 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) + + /* 7 is highest priority & 0 is lowest. */ + if (pdata->chan_priority == CHAN_PRIORITY_ASCENDING) +- dwc->priority = r; ++ dwc->priority = nr_channels - i - 1; + else + dwc->priority = i; + +@@ -1622,6 +1621,7 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) + /* Hardware configuration */ + if (autocfg) { + unsigned int dwc_params; ++ unsigned int r = DW_DMA_MAX_NR_CHANNELS - i - 1; + void __iomem *addr = chip->regs + r * sizeof(u32); + + dwc_params = dma_read_byaddr(addr, DWC_PARAMS); +diff --git a/drivers/dma/pxa_dma.c b/drivers/dma/pxa_dma.c +index ddcbbf5cd9e9..95bdbbe2a671 100644 +--- a/drivers/dma/pxa_dma.c ++++ b/drivers/dma/pxa_dma.c +@@ -888,6 +888,7 @@ pxad_tx_prep(struct virt_dma_chan *vc, struct virt_dma_desc *vd, + struct dma_async_tx_descriptor *tx; + struct pxad_chan *chan = container_of(vc, struct pxad_chan, vc); + ++ INIT_LIST_HEAD(&vd->node); + tx = vchan_tx_prep(vc, vd, tx_flags); + tx->tx_submit = pxad_tx_submit; + dev_dbg(&chan->vc.chan.dev->device, +diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c +index 43b57b02d050..ca94f475fd05 100644 +--- a/drivers/extcon/extcon.c ++++ b/drivers/extcon/extcon.c +@@ -126,7 +126,7 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id + + static int find_cable_id_by_name(struct extcon_dev *edev, const char *name) + { +- unsigned int id = -EINVAL; ++ int id = -EINVAL; + int i = 0; + + /* Find the id of extcon cable */ +@@ -143,7 +143,7 @@ static int find_cable_id_by_name(struct extcon_dev *edev, const char *name) + + static int find_cable_index_by_name(struct extcon_dev *edev, const char *name) + { +- unsigned int id; ++ int id; + + if (edev->max_supported == 0) + return -EINVAL; +@@ -159,7 +159,7 @@ static int find_cable_index_by_name(struct extcon_dev *edev, const char *name) + static bool is_extcon_changed(u32 prev, u32 new, int idx, bool *attached) + { + if (((prev >> idx) & 0x1) != ((new >> idx) & 0x1)) { +- *attached = new ? true : false; ++ *attached = ((new >> idx) & 0x1) ? true : false; + return true; + } + +@@ -378,7 +378,7 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state_); + */ + int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name) + { +- unsigned int id; ++ int id; + + id = find_cable_id_by_name(edev, cable_name); + if (id < 0) +@@ -426,7 +426,7 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_); + int extcon_set_cable_state(struct extcon_dev *edev, + const char *cable_name, bool cable_state) + { +- unsigned int id; ++ int id; + + id = find_cable_id_by_name(edev, cable_name); + if (id < 0) +diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c +index e29560e6b40b..950c87f5d279 100644 +--- a/drivers/firmware/efi/libstub/arm-stub.c ++++ b/drivers/firmware/efi/libstub/arm-stub.c +@@ -13,6 +13,7 @@ + */ + + #include ++#include + #include + + #include "efistub.h" +@@ -305,6 +306,44 @@ fail: + */ + #define EFI_RT_VIRTUAL_BASE 0x40000000 + ++static int cmp_mem_desc(const void *l, const void *r) ++{ ++ const efi_memory_desc_t *left = l, *right = r; ++ ++ return (left->phys_addr > right->phys_addr) ? 1 : -1; ++} ++ ++/* ++ * Returns whether region @left ends exactly where region @right starts, ++ * or false if either argument is NULL. ++ */ ++static bool regions_are_adjacent(efi_memory_desc_t *left, ++ efi_memory_desc_t *right) ++{ ++ u64 left_end; ++ ++ if (left == NULL || right == NULL) ++ return false; ++ ++ left_end = left->phys_addr + left->num_pages * EFI_PAGE_SIZE; ++ ++ return left_end == right->phys_addr; ++} ++ ++/* ++ * Returns whether region @left and region @right have compatible memory type ++ * mapping attributes, and are both EFI_MEMORY_RUNTIME regions. ++ */ ++static bool regions_have_compatible_memory_type_attrs(efi_memory_desc_t *left, ++ efi_memory_desc_t *right) ++{ ++ static const u64 mem_type_mask = EFI_MEMORY_WB | EFI_MEMORY_WT | ++ EFI_MEMORY_WC | EFI_MEMORY_UC | ++ EFI_MEMORY_RUNTIME; ++ ++ return ((left->attribute ^ right->attribute) & mem_type_mask) == 0; ++} ++ + /* + * efi_get_virtmap() - create a virtual mapping for the EFI memory map + * +@@ -317,33 +356,52 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, + int *count) + { + u64 efi_virt_base = EFI_RT_VIRTUAL_BASE; +- efi_memory_desc_t *out = runtime_map; ++ efi_memory_desc_t *in, *prev = NULL, *out = runtime_map; + int l; + +- for (l = 0; l < map_size; l += desc_size) { +- efi_memory_desc_t *in = (void *)memory_map + l; ++ /* ++ * To work around potential issues with the Properties Table feature ++ * introduced in UEFI 2.5, which may split PE/COFF executable images ++ * in memory into several RuntimeServicesCode and RuntimeServicesData ++ * regions, we need to preserve the relative offsets between adjacent ++ * EFI_MEMORY_RUNTIME regions with the same memory type attributes. ++ * The easiest way to find adjacent regions is to sort the memory map ++ * before traversing it. ++ */ ++ sort(memory_map, map_size / desc_size, desc_size, cmp_mem_desc, NULL); ++ ++ for (l = 0; l < map_size; l += desc_size, prev = in) { + u64 paddr, size; + ++ in = (void *)memory_map + l; + if (!(in->attribute & EFI_MEMORY_RUNTIME)) + continue; + ++ paddr = in->phys_addr; ++ size = in->num_pages * EFI_PAGE_SIZE; ++ + /* + * Make the mapping compatible with 64k pages: this allows + * a 4k page size kernel to kexec a 64k page size kernel and + * vice versa. + */ +- paddr = round_down(in->phys_addr, SZ_64K); +- size = round_up(in->num_pages * EFI_PAGE_SIZE + +- in->phys_addr - paddr, SZ_64K); +- +- /* +- * Avoid wasting memory on PTEs by choosing a virtual base that +- * is compatible with section mappings if this region has the +- * appropriate size and physical alignment. (Sections are 2 MB +- * on 4k granule kernels) +- */ +- if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) +- efi_virt_base = round_up(efi_virt_base, SZ_2M); ++ if (!regions_are_adjacent(prev, in) || ++ !regions_have_compatible_memory_type_attrs(prev, in)) { ++ ++ paddr = round_down(in->phys_addr, SZ_64K); ++ size += in->phys_addr - paddr; ++ ++ /* ++ * Avoid wasting memory on PTEs by choosing a virtual ++ * base that is compatible with section mappings if this ++ * region has the appropriate size and physical ++ * alignment. (Sections are 2 MB on 4k granule kernels) ++ */ ++ if (IS_ALIGNED(in->phys_addr, SZ_2M) && size >= SZ_2M) ++ efi_virt_base = round_up(efi_virt_base, SZ_2M); ++ else ++ efi_virt_base = round_up(efi_virt_base, SZ_64K); ++ } + + in->virt_addr = efi_virt_base + in->phys_addr - paddr; + efi_virt_base += size; +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +index b4d36f0f2153..c098d762089c 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +@@ -140,7 +140,7 @@ void amdgpu_irq_preinstall(struct drm_device *dev) + */ + int amdgpu_irq_postinstall(struct drm_device *dev) + { +- dev->max_vblank_count = 0x001fffff; ++ dev->max_vblank_count = 0x00ffffff; + return 0; + } + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +index 2abc661845b6..ddcfbf3b188b 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +@@ -543,46 +543,60 @@ static int amdgpu_uvd_cs_msg(struct amdgpu_uvd_cs_ctx *ctx, + return -EINVAL; + } + +- if (msg_type == 1) { ++ switch (msg_type) { ++ case 0: ++ /* it's a create msg, calc image size (width * height) */ ++ amdgpu_bo_kunmap(bo); ++ ++ /* try to alloc a new handle */ ++ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { ++ if (atomic_read(&adev->uvd.handles[i]) == handle) { ++ DRM_ERROR("Handle 0x%x already in use!\n", handle); ++ return -EINVAL; ++ } ++ ++ if (!atomic_cmpxchg(&adev->uvd.handles[i], 0, handle)) { ++ adev->uvd.filp[i] = ctx->parser->filp; ++ return 0; ++ } ++ } ++ ++ DRM_ERROR("No more free UVD handles!\n"); ++ return -EINVAL; ++ ++ case 1: + /* it's a decode msg, calc buffer sizes */ + r = amdgpu_uvd_cs_msg_decode(msg, ctx->buf_sizes); + amdgpu_bo_kunmap(bo); + if (r) + return r; + +- } else if (msg_type == 2) { ++ /* validate the handle */ ++ for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { ++ if (atomic_read(&adev->uvd.handles[i]) == handle) { ++ if (adev->uvd.filp[i] != ctx->parser->filp) { ++ DRM_ERROR("UVD handle collision detected!\n"); ++ return -EINVAL; ++ } ++ return 0; ++ } ++ } ++ ++ DRM_ERROR("Invalid UVD handle 0x%x!\n", handle); ++ return -ENOENT; ++ ++ case 2: + /* it's a destroy msg, free the handle */ + for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) + atomic_cmpxchg(&adev->uvd.handles[i], handle, 0); + amdgpu_bo_kunmap(bo); + return 0; +- } else { +- /* it's a create msg */ +- amdgpu_bo_kunmap(bo); +- +- if (msg_type != 0) { +- DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); +- return -EINVAL; +- } +- +- /* it's a create msg, no special handling needed */ +- } +- +- /* create or decode, validate the handle */ +- for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { +- if (atomic_read(&adev->uvd.handles[i]) == handle) +- return 0; +- } + +- /* handle not found try to alloc a new one */ +- for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) { +- if (!atomic_cmpxchg(&adev->uvd.handles[i], 0, handle)) { +- adev->uvd.filp[i] = ctx->parser->filp; +- return 0; +- } ++ default: ++ DRM_ERROR("Illegal UVD message type (%d)!\n", msg_type); ++ return -EINVAL; + } +- +- DRM_ERROR("No more free UVD handles!\n"); ++ BUG(); + return -EINVAL; + } + +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +index 9a4e3b63f1cb..b07402fc8ded 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +@@ -787,7 +787,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev, + int r; + + if (mem) { +- addr = mem->start << PAGE_SHIFT; ++ addr = (u64)mem->start << PAGE_SHIFT; + if (mem->mem_type != TTM_PL_TT) + addr += adev->vm_manager.vram_base_offset; + } else { +diff --git a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c +index ae8caca61e04..e60557417049 100644 +--- a/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c ++++ b/drivers/gpu/drm/amd/amdgpu/atombios_encoders.c +@@ -1279,8 +1279,7 @@ amdgpu_atombios_encoder_setup_dig(struct drm_encoder *encoder, int action) + amdgpu_atombios_encoder_setup_dig_encoder(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); + } + if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) +- amdgpu_atombios_encoder_setup_dig_transmitter(encoder, +- ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); ++ amdgpu_atombios_encoder_set_backlight_level(amdgpu_encoder, dig->backlight_level); + if (ext_encoder) + amdgpu_atombios_encoder_setup_external_encoder(encoder, ext_encoder, ATOM_ENABLE); + } else { +diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +index 4efd671d7a9b..9488ea6ea93f 100644 +--- a/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c ++++ b/drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c +@@ -224,11 +224,11 @@ static int uvd_v4_2_suspend(void *handle) + int r; + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + +- r = uvd_v4_2_hw_fini(adev); ++ r = amdgpu_uvd_suspend(adev); + if (r) + return r; + +- r = amdgpu_uvd_suspend(adev); ++ r = uvd_v4_2_hw_fini(adev); + if (r) + return r; + +diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +index b756bd99c0fd..d0ed998228ef 100644 +--- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +@@ -220,11 +220,11 @@ static int uvd_v5_0_suspend(void *handle) + int r; + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + +- r = uvd_v5_0_hw_fini(adev); ++ r = amdgpu_uvd_suspend(adev); + if (r) + return r; + +- r = amdgpu_uvd_suspend(adev); ++ r = uvd_v5_0_hw_fini(adev); + if (r) + return r; + +diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +index 49aa931b2cb4..345eb760fd5b 100644 +--- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +@@ -214,11 +214,11 @@ static int uvd_v6_0_suspend(void *handle) + int r; + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + +- r = uvd_v6_0_hw_fini(adev); ++ r = amdgpu_uvd_suspend(adev); + if (r) + return r; + +- r = amdgpu_uvd_suspend(adev); ++ r = uvd_v6_0_hw_fini(adev); + if (r) + return r; + +diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c +index 68552da40287..4f58a1e18de6 100644 +--- a/drivers/gpu/drm/amd/amdgpu/vi.c ++++ b/drivers/gpu/drm/amd/amdgpu/vi.c +@@ -1290,7 +1290,8 @@ static int vi_common_early_init(void *handle) + case CHIP_CARRIZO: + adev->has_uvd = true; + adev->cg_flags = 0; +- adev->pg_flags = AMDGPU_PG_SUPPORT_UVD | AMDGPU_PG_SUPPORT_VCE; ++ /* Disable UVD pg */ ++ adev->pg_flags = /* AMDGPU_PG_SUPPORT_UVD | */AMDGPU_PG_SUPPORT_VCE; + adev->external_rev_id = adev->rev_id + 0x1; + if (amdgpu_smc_load_fw && smc_enabled) + adev->firmware.smu_load = true; +diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c +index eb603f1defc2..969e7898a7ed 100644 +--- a/drivers/gpu/drm/drm_dp_mst_topology.c ++++ b/drivers/gpu/drm/drm_dp_mst_topology.c +@@ -804,8 +804,6 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref) + struct drm_dp_mst_port *port, *tmp; + bool wake_tx = false; + +- cancel_work_sync(&mstb->mgr->work); +- + /* + * destroy all ports - don't need lock + * as there are no more references to the mst branch +@@ -863,29 +861,33 @@ static void drm_dp_destroy_port(struct kref *kref) + { + struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref); + struct drm_dp_mst_topology_mgr *mgr = port->mgr; ++ + if (!port->input) { + port->vcpi.num_slots = 0; + + kfree(port->cached_edid); + +- /* we can't destroy the connector here, as +- we might be holding the mode_config.mutex +- from an EDID retrieval */ ++ /* ++ * The only time we don't have a connector ++ * on an output port is if the connector init ++ * fails. ++ */ + if (port->connector) { ++ /* we can't destroy the connector here, as ++ * we might be holding the mode_config.mutex ++ * from an EDID retrieval */ ++ + mutex_lock(&mgr->destroy_connector_lock); + list_add(&port->next, &mgr->destroy_connector_list); + mutex_unlock(&mgr->destroy_connector_lock); + schedule_work(&mgr->destroy_connector_work); + return; + } ++ /* no need to clean up vcpi ++ * as if we have no connector we never setup a vcpi */ + drm_dp_port_teardown_pdt(port, port->pdt); +- +- if (!port->input && port->vcpi.vcpi > 0) +- drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + } + kfree(port); +- +- (*mgr->cbs->hotplug)(mgr); + } + + static void drm_dp_put_port(struct drm_dp_mst_port *port) +@@ -1115,12 +1117,21 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, + char proppath[255]; + build_mst_prop_path(port, mstb, proppath, sizeof(proppath)); + port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); +- ++ if (!port->connector) { ++ /* remove it from the port list */ ++ mutex_lock(&mstb->mgr->lock); ++ list_del(&port->next); ++ mutex_unlock(&mstb->mgr->lock); ++ /* drop port list reference */ ++ drm_dp_put_port(port); ++ goto out; ++ } + if (port->port_num >= 8) { + port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc); + } + } + ++out: + /* put reference to this port */ + drm_dp_put_port(port); + } +@@ -1978,6 +1989,8 @@ void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr) + drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | DP_UPSTREAM_IS_SRC); + mutex_unlock(&mgr->lock); ++ flush_work(&mgr->work); ++ flush_work(&mgr->destroy_connector_work); + } + EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend); + +@@ -2661,7 +2674,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work) + { + struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work); + struct drm_dp_mst_port *port; +- ++ bool send_hotplug = false; + /* + * Not a regular list traverse as we have to drop the destroy + * connector lock before destroying the connector, to avoid AB->BA +@@ -2684,7 +2697,10 @@ static void drm_dp_destroy_connector_work(struct work_struct *work) + if (!port->input && port->vcpi.vcpi > 0) + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + kfree(port); ++ send_hotplug = true; + } ++ if (send_hotplug) ++ (*mgr->cbs->hotplug)(mgr); + } + + /** +@@ -2737,6 +2753,7 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init); + */ + void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) + { ++ flush_work(&mgr->work); + flush_work(&mgr->destroy_connector_work); + mutex_lock(&mgr->payload_lock); + kfree(mgr->payloads); +diff --git a/drivers/gpu/drm/drm_lock.c b/drivers/gpu/drm/drm_lock.c +index f861361a635e..4924d381b664 100644 +--- a/drivers/gpu/drm/drm_lock.c ++++ b/drivers/gpu/drm/drm_lock.c +@@ -61,6 +61,9 @@ int drm_legacy_lock(struct drm_device *dev, void *data, + struct drm_master *master = file_priv->master; + int ret = 0; + ++ if (drm_core_check_feature(dev, DRIVER_MODESET)) ++ return -EINVAL; ++ + ++file_priv->lock_count; + + if (lock->context == DRM_KERNEL_CONTEXT) { +@@ -153,6 +156,9 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_ + struct drm_lock *lock = data; + struct drm_master *master = file_priv->master; + ++ if (drm_core_check_feature(dev, DRIVER_MODESET)) ++ return -EINVAL; ++ + if (lock->context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + task_pid_nr(current), lock->context); +diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c +index 198fc3c3291b..17522f733513 100644 +--- a/drivers/gpu/drm/i915/intel_bios.c ++++ b/drivers/gpu/drm/i915/intel_bios.c +@@ -42,7 +42,7 @@ find_section(const void *_bdb, int section_id) + const struct bdb_header *bdb = _bdb; + const u8 *base = _bdb; + int index = 0; +- u16 total, current_size; ++ u32 total, current_size; + u8 current_id; + + /* skip to first section */ +@@ -57,6 +57,10 @@ find_section(const void *_bdb, int section_id) + current_size = *((const u16 *)(base + index)); + index += 2; + ++ /* The MIPI Sequence Block v3+ has a separate size field. */ ++ if (current_id == BDB_MIPI_SEQUENCE && *(base + index) >= 3) ++ current_size = *((const u32 *)(base + index + 1)); ++ + if (index + current_size > total) + return NULL; + +@@ -859,6 +863,12 @@ parse_mipi(struct drm_i915_private *dev_priv, const struct bdb_header *bdb) + return; + } + ++ /* Fail gracefully for forward incompatible sequence block. */ ++ if (sequence->version >= 3) { ++ DRM_ERROR("Unable to parse MIPI Sequence Block v3+\n"); ++ return; ++ } ++ + DRM_DEBUG_DRIVER("Found MIPI sequence block\n"); + + block_size = get_blocksize(sequence); +diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c +index 7c6225c84ba6..4649bd2ed340 100644 +--- a/drivers/gpu/drm/qxl/qxl_display.c ++++ b/drivers/gpu/drm/qxl/qxl_display.c +@@ -618,7 +618,7 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc, + adjusted_mode->hdisplay, + adjusted_mode->vdisplay); + +- if (qcrtc->index == 0) ++ if (bo->is_primary == false) + recreate_primary = true; + + if (bo->surf.stride * bo->surf.height > qdev->vram_size) { +@@ -886,13 +886,15 @@ static enum drm_connector_status qxl_conn_detect( + drm_connector_to_qxl_output(connector); + struct drm_device *ddev = connector->dev; + struct qxl_device *qdev = ddev->dev_private; +- int connected; ++ bool connected = false; + + /* The first monitor is always connected */ +- connected = (output->index == 0) || +- (qdev->client_monitors_config && +- qdev->client_monitors_config->count > output->index && +- qxl_head_enabled(&qdev->client_monitors_config->heads[output->index])); ++ if (!qdev->client_monitors_config) { ++ if (output->index == 0) ++ connected = true; ++ } else ++ connected = qdev->client_monitors_config->count > output->index && ++ qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]); + + DRM_DEBUG("#%d connected: %d\n", output->index, connected); + if (!connected) +diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c +index c3872598b85a..65adb9c72377 100644 +--- a/drivers/gpu/drm/radeon/atombios_encoders.c ++++ b/drivers/gpu/drm/radeon/atombios_encoders.c +@@ -1624,8 +1624,9 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode) + } else + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { +- args.ucAction = ATOM_LCD_BLON; +- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); ++ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; ++ ++ atombios_set_backlight_level(radeon_encoder, dig->backlight_level); + } + break; + case DRM_MODE_DPMS_STANDBY: +@@ -1706,8 +1707,7 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); + } + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) +- atombios_dig_transmitter_setup(encoder, +- ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); ++ atombios_set_backlight_level(radeon_encoder, dig->backlight_level); + if (ext_encoder) + atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE); + break; +diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c +index ea7ba5ef16a9..6a9d80a5332d 100644 +--- a/drivers/hv/hv_utils_transport.c ++++ b/drivers/hv/hv_utils_transport.c +@@ -186,7 +186,7 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) + return -EINVAL; + } else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) { + cn_msg = kzalloc(sizeof(*cn_msg) + len, GFP_ATOMIC); +- if (!msg) ++ if (!cn_msg) + return -ENOMEM; + cn_msg->id.idx = hvt->cn_id.idx; + cn_msg->id.val = hvt->cn_id.val; +diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c +index bd1c99deac71..2aaedbe0b023 100644 +--- a/drivers/hwmon/nct6775.c ++++ b/drivers/hwmon/nct6775.c +@@ -354,6 +354,10 @@ static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1] + + /* NCT6776 specific data */ + ++/* STEP_UP_TIME and STEP_DOWN_TIME regs are swapped for all chips but NCT6775 */ ++#define NCT6776_REG_FAN_STEP_UP_TIME NCT6775_REG_FAN_STEP_DOWN_TIME ++#define NCT6776_REG_FAN_STEP_DOWN_TIME NCT6775_REG_FAN_STEP_UP_TIME ++ + static const s8 NCT6776_ALARM_BITS[] = { + 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ + 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ +@@ -3528,8 +3532,8 @@ static int nct6775_probe(struct platform_device *pdev) + data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; +- data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; +- data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; ++ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; ++ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; +@@ -3600,8 +3604,8 @@ static int nct6775_probe(struct platform_device *pdev) + data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; +- data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; +- data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; ++ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; ++ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; +@@ -3677,8 +3681,8 @@ static int nct6775_probe(struct platform_device *pdev) + data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; +- data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; +- data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; ++ data->REG_FAN_TIME[1] = NCT6776_REG_FAN_STEP_UP_TIME; ++ data->REG_FAN_TIME[2] = NCT6776_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6775_REG_PWM; + data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; +diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c +index d851e1828d6f..85761b78bb5f 100644 +--- a/drivers/infiniband/ulp/isert/ib_isert.c ++++ b/drivers/infiniband/ulp/isert/ib_isert.c +@@ -3012,9 +3012,16 @@ isert_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd, bool recovery) + static int + isert_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state) + { +- int ret; ++ struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); ++ int ret = 0; + + switch (state) { ++ case ISTATE_REMOVE: ++ spin_lock_bh(&conn->cmd_lock); ++ list_del_init(&cmd->i_conn_node); ++ spin_unlock_bh(&conn->cmd_lock); ++ isert_put_cmd(isert_cmd, true); ++ break; + case ISTATE_SEND_NOPIN_WANT_RESPONSE: + ret = isert_put_nopin(cmd, conn, false); + break; +@@ -3379,6 +3386,41 @@ isert_wait4flush(struct isert_conn *isert_conn) + wait_for_completion(&isert_conn->wait_comp_err); + } + ++/** ++ * isert_put_unsol_pending_cmds() - Drop commands waiting for ++ * unsolicitate dataout ++ * @conn: iscsi connection ++ * ++ * We might still have commands that are waiting for unsolicited ++ * dataouts messages. We must put the extra reference on those ++ * before blocking on the target_wait_for_session_cmds ++ */ ++static void ++isert_put_unsol_pending_cmds(struct iscsi_conn *conn) ++{ ++ struct iscsi_cmd *cmd, *tmp; ++ static LIST_HEAD(drop_cmd_list); ++ ++ spin_lock_bh(&conn->cmd_lock); ++ list_for_each_entry_safe(cmd, tmp, &conn->conn_cmd_list, i_conn_node) { ++ if ((cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA) && ++ (cmd->write_data_done < conn->sess->sess_ops->FirstBurstLength) && ++ (cmd->write_data_done < cmd->se_cmd.data_length)) ++ list_move_tail(&cmd->i_conn_node, &drop_cmd_list); ++ } ++ spin_unlock_bh(&conn->cmd_lock); ++ ++ list_for_each_entry_safe(cmd, tmp, &drop_cmd_list, i_conn_node) { ++ list_del_init(&cmd->i_conn_node); ++ if (cmd->i_state != ISTATE_REMOVE) { ++ struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); ++ ++ isert_info("conn %p dropping cmd %p\n", conn, cmd); ++ isert_put_cmd(isert_cmd, true); ++ } ++ } ++} ++ + static void isert_wait_conn(struct iscsi_conn *conn) + { + struct isert_conn *isert_conn = conn->context; +@@ -3397,8 +3439,9 @@ static void isert_wait_conn(struct iscsi_conn *conn) + isert_conn_terminate(isert_conn); + mutex_unlock(&isert_conn->mutex); + +- isert_wait4cmds(conn); + isert_wait4flush(isert_conn); ++ isert_put_unsol_pending_cmds(conn); ++ isert_wait4cmds(conn); + isert_wait4logout(isert_conn); + + queue_work(isert_release_wq, &isert_conn->release_work); +diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c +index 459bf4429d36..7e077bf13fe1 100644 +--- a/drivers/irqchip/irq-atmel-aic5.c ++++ b/drivers/irqchip/irq-atmel-aic5.c +@@ -88,28 +88,36 @@ static void aic5_mask(struct irq_data *d) + { + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; +- struct irq_chip_generic *gc = dgc->gc[0]; ++ struct irq_chip_generic *bgc = dgc->gc[0]; ++ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + +- /* Disable interrupt on AIC5 */ +- irq_gc_lock(gc); ++ /* ++ * Disable interrupt on AIC5. We always take the lock of the ++ * first irq chip as all chips share the same registers. ++ */ ++ irq_gc_lock(bgc); + irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); + irq_reg_writel(gc, 1, AT91_AIC5_IDCR); + gc->mask_cache &= ~d->mask; +- irq_gc_unlock(gc); ++ irq_gc_unlock(bgc); + } + + static void aic5_unmask(struct irq_data *d) + { + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; +- struct irq_chip_generic *gc = dgc->gc[0]; ++ struct irq_chip_generic *bgc = dgc->gc[0]; ++ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + +- /* Enable interrupt on AIC5 */ +- irq_gc_lock(gc); ++ /* ++ * Enable interrupt on AIC5. We always take the lock of the ++ * first irq chip as all chips share the same registers. ++ */ ++ irq_gc_lock(bgc); + irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR); + irq_reg_writel(gc, 1, AT91_AIC5_IECR); + gc->mask_cache |= d->mask; +- irq_gc_unlock(gc); ++ irq_gc_unlock(bgc); + } + + static int aic5_retrigger(struct irq_data *d) +diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c +index c00e2db351ba..9a791dd52199 100644 +--- a/drivers/irqchip/irq-gic-v3-its.c ++++ b/drivers/irqchip/irq-gic-v3-its.c +@@ -921,8 +921,10 @@ retry_baser: + * non-cacheable as well. + */ + shr = tmp & GITS_BASER_SHAREABILITY_MASK; +- if (!shr) ++ if (!shr) { + cache = GITS_BASER_nC; ++ __flush_dcache_area(base, alloc_size); ++ } + goto retry_baser; + } + +@@ -1163,6 +1165,8 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, + return NULL; + } + ++ __flush_dcache_area(itt, sz); ++ + dev->its = its; + dev->itt = itt; + dev->nr_ites = nr_ites; +diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig +index 9ad35f72ab4c..433fb9df848a 100644 +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -229,7 +229,7 @@ config LEDS_LP55XX_COMMON + tristate "Common Driver for TI/National LP5521/5523/55231/5562/8501" + depends on LEDS_LP5521 || LEDS_LP5523 || LEDS_LP5562 || LEDS_LP8501 + select FW_LOADER +- select FW_LOADER_USER_HELPER_FALLBACK ++ select FW_LOADER_USER_HELPER + help + This option supports common operations for LP5521/5523/55231/5562/8501 + devices. +diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c +index beabfbc6f7cd..ca51d58bed24 100644 +--- a/drivers/leds/led-class.c ++++ b/drivers/leds/led-class.c +@@ -228,12 +228,15 @@ static int led_classdev_next_name(const char *init_name, char *name, + { + unsigned int i = 0; + int ret = 0; ++ struct device *dev; + + strlcpy(name, init_name, len); + +- while (class_find_device(leds_class, NULL, name, match_name) && +- (ret < len)) ++ while ((ret < len) && ++ (dev = class_find_device(leds_class, NULL, name, match_name))) { ++ put_device(dev); + ret = snprintf(name, len, "%s_%u", init_name, ++i); ++ } + + if (ret >= len) + return -ENOMEM; +diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c +index 3ee198b65843..cc7ece1712b5 100644 +--- a/drivers/macintosh/windfarm_core.c ++++ b/drivers/macintosh/windfarm_core.c +@@ -435,7 +435,7 @@ int wf_unregister_client(struct notifier_block *nb) + { + mutex_lock(&wf_lock); + blocking_notifier_chain_unregister(&wf_client_list, nb); +- wf_client_count++; ++ wf_client_count--; + if (wf_client_count == 0) + wf_stop_thread(); + mutex_unlock(&wf_lock); +diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c +index e51de52eeb94..48b5890c28e3 100644 +--- a/drivers/md/bitmap.c ++++ b/drivers/md/bitmap.c +@@ -1997,7 +1997,8 @@ int bitmap_resize(struct bitmap *bitmap, sector_t blocks, + if (bitmap->mddev->bitmap_info.offset || bitmap->mddev->bitmap_info.file) + ret = bitmap_storage_alloc(&store, chunks, + !bitmap->mddev->bitmap_info.external, +- bitmap->cluster_slot); ++ mddev_is_clustered(bitmap->mddev) ++ ? bitmap->cluster_slot : 0); + if (ret) + goto err; + +diff --git a/drivers/md/dm-cache-policy-cleaner.c b/drivers/md/dm-cache-policy-cleaner.c +index 240c9f0e85e7..8a096456579b 100644 +--- a/drivers/md/dm-cache-policy-cleaner.c ++++ b/drivers/md/dm-cache-policy-cleaner.c +@@ -436,7 +436,7 @@ static struct dm_cache_policy *wb_create(dm_cblock_t cache_size, + static struct dm_cache_policy_type wb_policy_type = { + .name = "cleaner", + .version = {1, 0, 0}, +- .hint_size = 0, ++ .hint_size = 4, + .owner = THIS_MODULE, + .create = wb_create + }; +diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c +index 0f48fed44a17..0d28c5b9d065 100644 +--- a/drivers/md/dm-crypt.c ++++ b/drivers/md/dm-crypt.c +@@ -968,7 +968,8 @@ static void crypt_free_buffer_pages(struct crypt_config *cc, struct bio *clone); + + /* + * Generate a new unfragmented bio with the given size +- * This should never violate the device limitations ++ * This should never violate the device limitations (but only because ++ * max_segment_size is being constrained to PAGE_SIZE). + * + * This function may be called concurrently. If we allocate from the mempool + * concurrently, there is a possibility of deadlock. For example, if we have +@@ -2058,9 +2059,20 @@ static int crypt_iterate_devices(struct dm_target *ti, + return fn(ti, cc->dev, cc->start, ti->len, data); + } + ++static void crypt_io_hints(struct dm_target *ti, struct queue_limits *limits) ++{ ++ /* ++ * Unfortunate constraint that is required to avoid the potential ++ * for exceeding underlying device's max_segments limits -- due to ++ * crypt_alloc_buffer() possibly allocating pages for the encryption ++ * bio that are not as physically contiguous as the original bio. ++ */ ++ limits->max_segment_size = PAGE_SIZE; ++} ++ + static struct target_type crypt_target = { + .name = "crypt", +- .version = {1, 14, 0}, ++ .version = {1, 14, 1}, + .module = THIS_MODULE, + .ctr = crypt_ctr, + .dtr = crypt_dtr, +@@ -2072,6 +2084,7 @@ static struct target_type crypt_target = { + .message = crypt_message, + .merge = crypt_merge, + .iterate_devices = crypt_iterate_devices, ++ .io_hints = crypt_io_hints, + }; + + static int __init dm_crypt_init(void) +diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c +index 2daa67793511..1257d484392a 100644 +--- a/drivers/md/dm-raid.c ++++ b/drivers/md/dm-raid.c +@@ -329,8 +329,7 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size) + */ + if (min_region_size > (1 << 13)) { + /* If not a power of 2, make it the next power of 2 */ +- if (min_region_size & (min_region_size - 1)) +- region_size = 1 << fls(region_size); ++ region_size = roundup_pow_of_two(min_region_size); + DMINFO("Choosing default region size of %lu sectors", + region_size); + } else { +diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c +index d2bbe8cc1e97..75aef240c2d1 100644 +--- a/drivers/md/dm-thin.c ++++ b/drivers/md/dm-thin.c +@@ -4333,6 +4333,10 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits) + { + struct thin_c *tc = ti->private; + struct pool *pool = tc->pool; ++ struct queue_limits *pool_limits = dm_get_queue_limits(pool->pool_md); ++ ++ if (!pool_limits->discard_granularity) ++ return; /* pool's discard support is disabled */ + + limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT; + limits->max_discard_sectors = 2048 * 1024 * 16; /* 16G */ +diff --git a/drivers/md/dm.c b/drivers/md/dm.c +index 0d7ab20c58df..3e32f4e31bbb 100644 +--- a/drivers/md/dm.c ++++ b/drivers/md/dm.c +@@ -2952,8 +2952,6 @@ static void __dm_destroy(struct mapped_device *md, bool wait) + + might_sleep(); + +- map = dm_get_live_table(md, &srcu_idx); +- + spin_lock(&_minor_lock); + idr_replace(&_minor_idr, MINOR_ALLOCED, MINOR(disk_devt(dm_disk(md)))); + set_bit(DMF_FREEING, &md->flags); +@@ -2967,14 +2965,14 @@ static void __dm_destroy(struct mapped_device *md, bool wait) + * do not race with internal suspend. + */ + mutex_lock(&md->suspend_lock); ++ map = dm_get_live_table(md, &srcu_idx); + if (!dm_suspended_md(md)) { + dm_table_presuspend_targets(map); + dm_table_postsuspend_targets(map); + } +- mutex_unlock(&md->suspend_lock); +- + /* dm_put_live_table must be before msleep, otherwise deadlock is possible */ + dm_put_live_table(md, srcu_idx); ++ mutex_unlock(&md->suspend_lock); + + /* + * Rare, but there may be I/O requests still going to complete, +diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c +index efb654eb5399..0875e5e7e09a 100644 +--- a/drivers/md/raid0.c ++++ b/drivers/md/raid0.c +@@ -83,7 +83,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) + char b[BDEVNAME_SIZE]; + char b2[BDEVNAME_SIZE]; + struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL); +- bool discard_supported = false; ++ unsigned short blksize = 512; + + if (!conf) + return -ENOMEM; +@@ -98,6 +98,9 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) + sector_div(sectors, mddev->chunk_sectors); + rdev1->sectors = sectors * mddev->chunk_sectors; + ++ blksize = max(blksize, queue_logical_block_size( ++ rdev1->bdev->bd_disk->queue)); ++ + rdev_for_each(rdev2, mddev) { + pr_debug("md/raid0:%s: comparing %s(%llu)" + " with %s(%llu)\n", +@@ -134,6 +137,18 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) + } + pr_debug("md/raid0:%s: FINAL %d zones\n", + mdname(mddev), conf->nr_strip_zones); ++ /* ++ * now since we have the hard sector sizes, we can make sure ++ * chunk size is a multiple of that sector size ++ */ ++ if ((mddev->chunk_sectors << 9) % blksize) { ++ printk(KERN_ERR "md/raid0:%s: chunk_size of %d not multiple of block size %d\n", ++ mdname(mddev), ++ mddev->chunk_sectors << 9, blksize); ++ err = -EINVAL; ++ goto abort; ++ } ++ + err = -ENOMEM; + conf->strip_zone = kzalloc(sizeof(struct strip_zone)* + conf->nr_strip_zones, GFP_KERNEL); +@@ -188,19 +203,12 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) + } + dev[j] = rdev1; + +- if (mddev->queue) +- disk_stack_limits(mddev->gendisk, rdev1->bdev, +- rdev1->data_offset << 9); +- + if (rdev1->bdev->bd_disk->queue->merge_bvec_fn) + conf->has_merge_bvec = 1; + + if (!smallest || (rdev1->sectors < smallest->sectors)) + smallest = rdev1; + cnt++; +- +- if (blk_queue_discard(bdev_get_queue(rdev1->bdev))) +- discard_supported = true; + } + if (cnt != mddev->raid_disks) { + printk(KERN_ERR "md/raid0:%s: too few disks (%d of %d) - " +@@ -261,28 +269,6 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) + (unsigned long long)smallest->sectors); + } + +- /* +- * now since we have the hard sector sizes, we can make sure +- * chunk size is a multiple of that sector size +- */ +- if ((mddev->chunk_sectors << 9) % queue_logical_block_size(mddev->queue)) { +- printk(KERN_ERR "md/raid0:%s: chunk_size of %d not valid\n", +- mdname(mddev), +- mddev->chunk_sectors << 9); +- goto abort; +- } +- +- if (mddev->queue) { +- blk_queue_io_min(mddev->queue, mddev->chunk_sectors << 9); +- blk_queue_io_opt(mddev->queue, +- (mddev->chunk_sectors << 9) * mddev->raid_disks); +- +- if (!discard_supported) +- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); +- else +- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); +- } +- + pr_debug("md/raid0:%s: done.\n", mdname(mddev)); + *private_conf = conf; + +@@ -433,12 +419,6 @@ static int raid0_run(struct mddev *mddev) + if (md_check_no_bitmap(mddev)) + return -EINVAL; + +- if (mddev->queue) { +- blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors); +- blk_queue_max_write_same_sectors(mddev->queue, mddev->chunk_sectors); +- blk_queue_max_discard_sectors(mddev->queue, mddev->chunk_sectors); +- } +- + /* if private is not null, we are here after takeover */ + if (mddev->private == NULL) { + ret = create_strip_zones(mddev, &conf); +@@ -447,6 +427,29 @@ static int raid0_run(struct mddev *mddev) + mddev->private = conf; + } + conf = mddev->private; ++ if (mddev->queue) { ++ struct md_rdev *rdev; ++ bool discard_supported = false; ++ ++ blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors); ++ blk_queue_max_write_same_sectors(mddev->queue, mddev->chunk_sectors); ++ blk_queue_max_discard_sectors(mddev->queue, mddev->chunk_sectors); ++ ++ blk_queue_io_min(mddev->queue, mddev->chunk_sectors << 9); ++ blk_queue_io_opt(mddev->queue, ++ (mddev->chunk_sectors << 9) * mddev->raid_disks); ++ ++ rdev_for_each(rdev, mddev) { ++ disk_stack_limits(mddev->gendisk, rdev->bdev, ++ rdev->data_offset << 9); ++ if (blk_queue_discard(bdev_get_queue(rdev->bdev))) ++ discard_supported = true; ++ } ++ if (!discard_supported) ++ queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); ++ else ++ queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); ++ } + + /* calculate array device size */ + md_set_array_sectors(mddev, raid0_size(mddev, 0, 0)); +diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c +index 9e3fdbdc4037..2f4503a7f315 100644 +--- a/drivers/mmc/core/core.c ++++ b/drivers/mmc/core/core.c +@@ -134,9 +134,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) + int err = cmd->error; + + /* Flag re-tuning needed on CRC errors */ +- if (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || ++ if ((cmd->opcode != MMC_SEND_TUNING_BLOCK && ++ cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) && ++ (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || + (mrq->data && mrq->data->error == -EILSEQ) || +- (mrq->stop && mrq->stop->error == -EILSEQ)) ++ (mrq->stop && mrq->stop->error == -EILSEQ))) + mmc_retune_needed(host); + + if (err && cmd->retries && mmc_host_is_spi(host)) { +diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c +index 99a9c9011c50..79979e9d5a09 100644 +--- a/drivers/mmc/core/host.c ++++ b/drivers/mmc/core/host.c +@@ -457,7 +457,7 @@ int mmc_of_parse(struct mmc_host *host) + 0, &cd_gpio_invert); + if (!ret) + dev_info(host->parent, "Got CD GPIO\n"); +- else if (ret != -ENOENT) ++ else if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + /* +@@ -481,7 +481,7 @@ int mmc_of_parse(struct mmc_host *host) + ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert); + if (!ret) + dev_info(host->parent, "Got WP GPIO\n"); +- else if (ret != -ENOENT) ++ else if (ret != -ENOENT && ret != -ENOSYS) + return ret; + + if (of_property_read_bool(np, "disable-wp")) +diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c +index 946d37f94a31..f5edf9d3a18a 100644 +--- a/drivers/mmc/host/sdhci-pxav3.c ++++ b/drivers/mmc/host/sdhci-pxav3.c +@@ -135,6 +135,7 @@ static int armada_38x_quirks(struct platform_device *pdev, + struct sdhci_pxa *pxa = pltfm_host->priv; + struct resource *res; + ++ host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; + host->quirks |= SDHCI_QUIRK_MISSING_CAPS; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "conf-sdio3"); +@@ -290,6 +291,9 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) + uhs == MMC_TIMING_UHS_DDR50) { + reg_val &= ~SDIO3_CONF_CLK_INV; + reg_val |= SDIO3_CONF_SD_FB_CLK; ++ } else if (uhs == MMC_TIMING_MMC_HS) { ++ reg_val &= ~SDIO3_CONF_CLK_INV; ++ reg_val &= ~SDIO3_CONF_SD_FB_CLK; + } else { + reg_val |= SDIO3_CONF_CLK_INV; + reg_val &= ~SDIO3_CONF_SD_FB_CLK; +@@ -398,7 +402,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + ret = armada_38x_quirks(pdev, host); + if (ret < 0) +- goto err_clk_get; ++ goto err_mbus_win; + ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); + if (ret < 0) + goto err_mbus_win; +diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c +index 1259cc558ce9..5465fa439c9e 100644 +--- a/drivers/mtd/nand/pxa3xx_nand.c ++++ b/drivers/mtd/nand/pxa3xx_nand.c +@@ -1473,6 +1473,9 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) + if (pdata->keep_config && !pxa3xx_nand_detect_config(info)) + goto KEEP_CONFIG; + ++ /* Set a default chunk size */ ++ info->chunk_size = 512; ++ + ret = pxa3xx_nand_sensing(info); + if (ret) { + dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", +diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c +index 6f93b2990d25..499b8e433d3d 100644 +--- a/drivers/mtd/nand/sunxi_nand.c ++++ b/drivers/mtd/nand/sunxi_nand.c +@@ -138,6 +138,10 @@ + #define NFC_ECC_MODE GENMASK(15, 12) + #define NFC_RANDOM_SEED GENMASK(30, 16) + ++/* NFC_USER_DATA helper macros */ ++#define NFC_BUF_TO_USER_DATA(buf) ((buf)[0] | ((buf)[1] << 8) | \ ++ ((buf)[2] << 16) | ((buf)[3] << 24)) ++ + #define NFC_DEFAULT_TIMEOUT_MS 1000 + + #define NFC_SRAM_SIZE 1024 +@@ -632,15 +636,9 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; + + /* Fill OOB data in */ +- if (oob_required) { +- tmp = 0xffffffff; +- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, +- 4); +- } else { +- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, +- chip->oob_poi + offset - mtd->writesize, +- 4); +- } ++ writel(NFC_BUF_TO_USER_DATA(chip->oob_poi + ++ layout->oobfree[i].offset), ++ nfc->regs + NFC_REG_USER_DATA_BASE); + + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + +@@ -770,14 +768,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + offset += ecc->size; + + /* Fill OOB data in */ +- if (oob_required) { +- tmp = 0xffffffff; +- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp, +- 4); +- } else { +- memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob, +- 4); +- } ++ writel(NFC_BUF_TO_USER_DATA(oob), ++ nfc->regs + NFC_REG_USER_DATA_BASE); + + tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | + (1 << 30); +@@ -1312,6 +1304,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) + node); + nand_release(&chip->mtd); + sunxi_nand_ecc_cleanup(&chip->nand.ecc); ++ list_del(&chip->node); + } + } + +diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c +index 5bbd1f094f4e..1fc23e48fe8e 100644 +--- a/drivers/mtd/ubi/io.c ++++ b/drivers/mtd/ubi/io.c +@@ -926,6 +926,11 @@ static int validate_vid_hdr(const struct ubi_device *ubi, + goto bad; + } + ++ if (data_size > ubi->leb_size) { ++ ubi_err(ubi, "bad data_size"); ++ goto bad; ++ } ++ + if (vol_type == UBI_VID_STATIC) { + /* + * Although from high-level point of view static volumes may +diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c +index 80bdd5b88bac..d85c19762160 100644 +--- a/drivers/mtd/ubi/vtbl.c ++++ b/drivers/mtd/ubi/vtbl.c +@@ -649,6 +649,7 @@ static int init_volumes(struct ubi_device *ubi, + if (ubi->corr_peb_count) + ubi_err(ubi, "%d PEBs are corrupted and not used", + ubi->corr_peb_count); ++ return -ENOSPC; + } + ubi->rsvd_pebs += reserved_pebs; + ubi->avail_pebs -= reserved_pebs; +diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c +index 275d9fb6fe5c..eb4489f9082f 100644 +--- a/drivers/mtd/ubi/wl.c ++++ b/drivers/mtd/ubi/wl.c +@@ -1601,6 +1601,7 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) + if (ubi->corr_peb_count) + ubi_err(ubi, "%d PEBs are corrupted and not used", + ubi->corr_peb_count); ++ err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= reserved_pebs; +diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c +index 89d788d8f263..adfe1de78d99 100644 +--- a/drivers/net/ethernet/intel/e1000e/netdev.c ++++ b/drivers/net/ethernet/intel/e1000e/netdev.c +@@ -4280,18 +4280,29 @@ static cycle_t e1000e_cyclecounter_read(const struct cyclecounter *cc) + struct e1000_adapter *adapter = container_of(cc, struct e1000_adapter, + cc); + struct e1000_hw *hw = &adapter->hw; ++ u32 systimel_1, systimel_2, systimeh; + cycle_t systim, systim_next; +- /* SYSTIMH latching upon SYSTIML read does not work well. To fix that +- * we don't want to allow overflow of SYSTIML and a change to SYSTIMH +- * to occur between reads, so if we read a vale close to overflow, we +- * wait for overflow to occur and read both registers when its safe. ++ /* SYSTIMH latching upon SYSTIML read does not work well. ++ * This means that if SYSTIML overflows after we read it but before ++ * we read SYSTIMH, the value of SYSTIMH has been incremented and we ++ * will experience a huge non linear increment in the systime value ++ * to fix that we test for overflow and if true, we re-read systime. + */ +- u32 systim_overflow_latch_fix = 0x3FFFFFFF; +- +- do { +- systim = (cycle_t)er32(SYSTIML); +- } while (systim > systim_overflow_latch_fix); +- systim |= (cycle_t)er32(SYSTIMH) << 32; ++ systimel_1 = er32(SYSTIML); ++ systimeh = er32(SYSTIMH); ++ systimel_2 = er32(SYSTIML); ++ /* Check for overflow. If there was no overflow, use the values */ ++ if (systimel_1 < systimel_2) { ++ systim = (cycle_t)systimel_1; ++ systim |= (cycle_t)systimeh << 32; ++ } else { ++ /* There was an overflow, read again SYSTIMH, and use ++ * systimel_2 ++ */ ++ systimeh = er32(SYSTIMH); ++ systim = (cycle_t)systimel_2; ++ systim |= (cycle_t)systimeh << 32; ++ } + + if ((hw->mac.type == e1000_82574) || (hw->mac.type == e1000_82583)) { + u64 incvalue, time_delta, rem, temp; +diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c +index 8d7b59689722..5bc9fca67957 100644 +--- a/drivers/net/ethernet/intel/igb/igb_main.c ++++ b/drivers/net/ethernet/intel/igb/igb_main.c +@@ -2851,7 +2851,7 @@ static void igb_probe_vfs(struct igb_adapter *adapter) + return; + + pci_sriov_set_totalvfs(pdev, 7); +- igb_pci_enable_sriov(pdev, max_vfs); ++ igb_enable_sriov(pdev, max_vfs); + + #endif /* CONFIG_PCI_IOV */ + } +diff --git a/drivers/net/ethernet/via/Kconfig b/drivers/net/ethernet/via/Kconfig +index 2f1264b882b9..d3d094742a7e 100644 +--- a/drivers/net/ethernet/via/Kconfig ++++ b/drivers/net/ethernet/via/Kconfig +@@ -17,7 +17,7 @@ if NET_VENDOR_VIA + + config VIA_RHINE + tristate "VIA Rhine support" +- depends on (PCI || OF_IRQ) ++ depends on PCI || (OF_IRQ && GENERIC_PCI_IOMAP) + depends on HAS_DMA + select CRC32 + select MII +diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c +index 85bfa2acb801..32d9ff1b19dc 100644 +--- a/drivers/net/wireless/ath/ath10k/htc.c ++++ b/drivers/net/wireless/ath/ath10k/htc.c +@@ -145,8 +145,10 @@ int ath10k_htc_send(struct ath10k_htc *htc, + skb_cb->eid = eid; + skb_cb->paddr = dma_map_single(dev, skb->data, skb->len, DMA_TO_DEVICE); + ret = dma_mapping_error(dev, skb_cb->paddr); +- if (ret) ++ if (ret) { ++ ret = -EIO; + goto err_credits; ++ } + + sg_item.transfer_id = ep->eid; + sg_item.transfer_context = skb; +diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c +index a60ef7d1d5fc..7be3ce6e0ffa 100644 +--- a/drivers/net/wireless/ath/ath10k/htt_tx.c ++++ b/drivers/net/wireless/ath/ath10k/htt_tx.c +@@ -371,8 +371,10 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, + DMA_TO_DEVICE); + res = dma_mapping_error(dev, skb_cb->paddr); +- if (res) ++ if (res) { ++ res = -EIO; + goto err_free_txdesc; ++ } + + skb_put(txdesc, len); + cmd = (struct htt_cmd *)txdesc->data; +@@ -456,8 +458,10 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, + DMA_TO_DEVICE); + res = dma_mapping_error(dev, skb_cb->paddr); +- if (res) ++ if (res) { ++ res = -EIO; + goto err_free_txbuf; ++ } + + switch (skb_cb->txmode) { + case ATH10K_HW_TXRX_RAW: +diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c +index 218b6af63447..0d3c474ff76d 100644 +--- a/drivers/net/wireless/ath/ath10k/mac.c ++++ b/drivers/net/wireless/ath/ath10k/mac.c +@@ -591,11 +591,19 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw, + static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr, + enum wmi_peer_type peer_type) + { ++ struct ath10k_vif *arvif; ++ int num_peers = 0; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + +- if (ar->num_peers >= ar->max_num_peers) ++ num_peers = ar->num_peers; ++ ++ /* Each vdev consumes a peer entry as well */ ++ list_for_each_entry(arvif, &ar->arvifs, list) ++ num_peers++; ++ ++ if (num_peers >= ar->max_num_peers) + return -ENOBUFS; + + ret = ath10k_wmi_peer_create(ar, vdev_id, addr, peer_type); +@@ -2995,6 +3003,8 @@ void ath10k_mac_tx_unlock(struct ath10k *ar, int reason) + IEEE80211_IFACE_ITER_RESUME_ALL, + ath10k_mac_tx_unlock_iter, + ar); ++ ++ ieee80211_wake_queue(ar->hw, ar->hw->offchannel_tx_hw_queue); + } + + void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason) +@@ -3034,38 +3044,16 @@ static void ath10k_mac_vif_handle_tx_pause(struct ath10k_vif *arvif, + + lockdep_assert_held(&ar->htt.tx_lock); + +- switch (pause_id) { +- case WMI_TLV_TX_PAUSE_ID_MCC: +- case WMI_TLV_TX_PAUSE_ID_P2P_CLI_NOA: +- case WMI_TLV_TX_PAUSE_ID_P2P_GO_PS: +- case WMI_TLV_TX_PAUSE_ID_AP_PS: +- case WMI_TLV_TX_PAUSE_ID_IBSS_PS: +- switch (action) { +- case WMI_TLV_TX_PAUSE_ACTION_STOP: +- ath10k_mac_vif_tx_lock(arvif, pause_id); +- break; +- case WMI_TLV_TX_PAUSE_ACTION_WAKE: +- ath10k_mac_vif_tx_unlock(arvif, pause_id); +- break; +- default: +- ath10k_warn(ar, "received unknown tx pause action %d on vdev %i, ignoring\n", +- action, arvif->vdev_id); +- break; +- } ++ switch (action) { ++ case WMI_TLV_TX_PAUSE_ACTION_STOP: ++ ath10k_mac_vif_tx_lock(arvif, pause_id); ++ break; ++ case WMI_TLV_TX_PAUSE_ACTION_WAKE: ++ ath10k_mac_vif_tx_unlock(arvif, pause_id); + break; +- case WMI_TLV_TX_PAUSE_ID_AP_PEER_PS: +- case WMI_TLV_TX_PAUSE_ID_AP_PEER_UAPSD: +- case WMI_TLV_TX_PAUSE_ID_STA_ADD_BA: +- case WMI_TLV_TX_PAUSE_ID_HOST: + default: +- /* FIXME: Some pause_ids aren't vdev specific. Instead they +- * target peer_id and tid. Implementing these could improve +- * traffic scheduling fairness across multiple connected +- * stations in AP/IBSS modes. +- */ +- ath10k_dbg(ar, ATH10K_DBG_MAC, +- "mac ignoring unsupported tx pause vdev %i id %d\n", +- arvif->vdev_id, pause_id); ++ ath10k_warn(ar, "received unknown tx pause action %d on vdev %i, ignoring\n", ++ action, arvif->vdev_id); + break; + } + } +@@ -3082,12 +3070,15 @@ static void ath10k_mac_handle_tx_pause_iter(void *data, u8 *mac, + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k_mac_tx_pause *arg = data; + ++ if (arvif->vdev_id != arg->vdev_id) ++ return; ++ + ath10k_mac_vif_handle_tx_pause(arvif, arg->pause_id, arg->action); + } + +-void ath10k_mac_handle_tx_pause(struct ath10k *ar, u32 vdev_id, +- enum wmi_tlv_tx_pause_id pause_id, +- enum wmi_tlv_tx_pause_action action) ++void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id, ++ enum wmi_tlv_tx_pause_id pause_id, ++ enum wmi_tlv_tx_pause_action action) + { + struct ath10k_mac_tx_pause arg = { + .vdev_id = vdev_id, +@@ -4080,6 +4071,11 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + ++ if (ar->num_peers >= ar->max_num_peers) { ++ ath10k_warn(ar, "refusing vdev creation due to insufficient peer entry resources in firmware\n"); ++ return -ENOBUFS; ++ } ++ + if (ar->free_vdev_map == 0) { + ath10k_warn(ar, "Free vdev map is empty, no more interfaces allowed.\n"); + ret = -EBUSY; +@@ -4287,6 +4283,11 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, + } + } + ++ spin_lock_bh(&ar->htt.tx_lock); ++ if (!ar->tx_paused) ++ ieee80211_wake_queue(ar->hw, arvif->vdev_id); ++ spin_unlock_bh(&ar->htt.tx_lock); ++ + mutex_unlock(&ar->conf_mutex); + return 0; + +@@ -5561,6 +5562,21 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) + return ret; + } + ++static int ath10k_mac_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) ++{ ++ /* Even though there's a WMI enum for fragmentation threshold no known ++ * firmware actually implements it. Moreover it is not possible to rely ++ * frame fragmentation to mac80211 because firmware clears the "more ++ * fragments" bit in frame control making it impossible for remote ++ * devices to reassemble frames. ++ * ++ * Hence implement a dummy callback just to say fragmentation isn't ++ * supported. This effectively prevents mac80211 from doing frame ++ * fragmentation in software. ++ */ ++ return -EOPNOTSUPP; ++} ++ + static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) + { +@@ -6395,6 +6411,7 @@ static const struct ieee80211_ops ath10k_ops = { + .remain_on_channel = ath10k_remain_on_channel, + .cancel_remain_on_channel = ath10k_cancel_remain_on_channel, + .set_rts_threshold = ath10k_set_rts_threshold, ++ .set_frag_threshold = ath10k_mac_op_set_frag_threshold, + .flush = ath10k_flush, + .tx_last_beacon = ath10k_tx_last_beacon, + .set_antenna = ath10k_set_antenna, +diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h +index b291f063705c..e3cefe4c7cfd 100644 +--- a/drivers/net/wireless/ath/ath10k/mac.h ++++ b/drivers/net/wireless/ath/ath10k/mac.h +@@ -61,9 +61,9 @@ int ath10k_mac_vif_chan(struct ieee80211_vif *vif, + + void ath10k_mac_handle_beacon(struct ath10k *ar, struct sk_buff *skb); + void ath10k_mac_handle_beacon_miss(struct ath10k *ar, u32 vdev_id); +-void ath10k_mac_handle_tx_pause(struct ath10k *ar, u32 vdev_id, +- enum wmi_tlv_tx_pause_id pause_id, +- enum wmi_tlv_tx_pause_action action); ++void ath10k_mac_handle_tx_pause_vdev(struct ath10k *ar, u32 vdev_id, ++ enum wmi_tlv_tx_pause_id pause_id, ++ enum wmi_tlv_tx_pause_action action); + + u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband, + u8 hw_rate); +diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c +index ea656e011a96..8c5cc1facc45 100644 +--- a/drivers/net/wireless/ath/ath10k/pci.c ++++ b/drivers/net/wireless/ath/ath10k/pci.c +@@ -1546,8 +1546,10 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, + + req_paddr = dma_map_single(ar->dev, treq, req_len, DMA_TO_DEVICE); + ret = dma_mapping_error(ar->dev, req_paddr); +- if (ret) ++ if (ret) { ++ ret = -EIO; + goto err_dma; ++ } + + if (resp && resp_len) { + tresp = kzalloc(*resp_len, GFP_KERNEL); +@@ -1559,8 +1561,10 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, + resp_paddr = dma_map_single(ar->dev, tresp, *resp_len, + DMA_FROM_DEVICE); + ret = dma_mapping_error(ar->dev, resp_paddr); +- if (ret) ++ if (ret) { ++ ret = EIO; + goto err_req; ++ } + + xfer.wait_for_resp = true; + xfer.resp_len = 0; +diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c +index 8fdba3865c96..6f477e83099d 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c ++++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c +@@ -377,12 +377,34 @@ static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar, + "wmi tlv tx pause pause_id %u action %u vdev_map 0x%08x peer_id %u tid_map 0x%08x\n", + pause_id, action, vdev_map, peer_id, tid_map); + +- for (vdev_id = 0; vdev_map; vdev_id++) { +- if (!(vdev_map & BIT(vdev_id))) +- continue; +- +- vdev_map &= ~BIT(vdev_id); +- ath10k_mac_handle_tx_pause(ar, vdev_id, pause_id, action); ++ switch (pause_id) { ++ case WMI_TLV_TX_PAUSE_ID_MCC: ++ case WMI_TLV_TX_PAUSE_ID_P2P_CLI_NOA: ++ case WMI_TLV_TX_PAUSE_ID_P2P_GO_PS: ++ case WMI_TLV_TX_PAUSE_ID_AP_PS: ++ case WMI_TLV_TX_PAUSE_ID_IBSS_PS: ++ for (vdev_id = 0; vdev_map; vdev_id++) { ++ if (!(vdev_map & BIT(vdev_id))) ++ continue; ++ ++ vdev_map &= ~BIT(vdev_id); ++ ath10k_mac_handle_tx_pause_vdev(ar, vdev_id, pause_id, ++ action); ++ } ++ break; ++ case WMI_TLV_TX_PAUSE_ID_AP_PEER_PS: ++ case WMI_TLV_TX_PAUSE_ID_AP_PEER_UAPSD: ++ case WMI_TLV_TX_PAUSE_ID_STA_ADD_BA: ++ case WMI_TLV_TX_PAUSE_ID_HOST: ++ ath10k_dbg(ar, ATH10K_DBG_MAC, ++ "mac ignoring unsupported tx pause id %d\n", ++ pause_id); ++ break; ++ default: ++ ath10k_dbg(ar, ATH10K_DBG_MAC, ++ "mac ignoring unknown tx pause vdev %d\n", ++ pause_id); ++ break; + } + + kfree(tb); +diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c +index 6c046c244705..8dd84c160cfd 100644 +--- a/drivers/net/wireless/ath/ath10k/wmi.c ++++ b/drivers/net/wireless/ath/ath10k/wmi.c +@@ -2391,6 +2391,7 @@ void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) + ath10k_warn(ar, "failed to map beacon: %d\n", + ret); + dev_kfree_skb_any(bcn); ++ ret = -EIO; + goto skip; + } + +diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +index 1c6788aecc62..40d72312f3df 100644 +--- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c ++++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +@@ -203,8 +203,10 @@ static int rsi_load_ta_instructions(struct rsi_common *common) + + /* Copy firmware into DMA-accessible memory */ + fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); +- if (!fw) +- return -ENOMEM; ++ if (!fw) { ++ status = -ENOMEM; ++ goto out; ++ } + len = fw_entry->size; + + if (len % 4) +@@ -217,6 +219,8 @@ static int rsi_load_ta_instructions(struct rsi_common *common) + + status = rsi_copy_to_card(common, fw, len, num_blocks); + kfree(fw); ++ ++out: + release_firmware(fw_entry); + return status; + } +diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c +index 30c2cf7fa93b..de4900862836 100644 +--- a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c ++++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c +@@ -148,8 +148,10 @@ static int rsi_load_ta_instructions(struct rsi_common *common) + + /* Copy firmware into DMA-accessible memory */ + fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); +- if (!fw) +- return -ENOMEM; ++ if (!fw) { ++ status = -ENOMEM; ++ goto out; ++ } + len = fw_entry->size; + + if (len % 4) +@@ -162,6 +164,8 @@ static int rsi_load_ta_instructions(struct rsi_common *common) + + status = rsi_copy_to_card(common, fw, len, num_blocks); + kfree(fw); ++ ++out: + release_firmware(fw_entry); + return status; + } +diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c +index f948c46d5132..5ff0cfd142ee 100644 +--- a/drivers/net/xen-netfront.c ++++ b/drivers/net/xen-netfront.c +@@ -1348,7 +1348,8 @@ static void xennet_disconnect_backend(struct netfront_info *info) + queue->tx_evtchn = queue->rx_evtchn = 0; + queue->tx_irq = queue->rx_irq = 0; + +- napi_synchronize(&queue->napi); ++ if (netif_running(info->netdev)) ++ napi_synchronize(&queue->napi); + + xennet_release_tx_bufs(queue); + xennet_release_rx_bufs(queue); +diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c +index ade9eb917a4d..b796d1bd8988 100644 +--- a/drivers/nvdimm/pmem.c ++++ b/drivers/nvdimm/pmem.c +@@ -86,6 +86,8 @@ static int pmem_rw_page(struct block_device *bdev, sector_t sector, + struct pmem_device *pmem = bdev->bd_disk->private_data; + + pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector); ++ if (rw & WRITE) ++ wmb_pmem(); + page_endio(page, rw & WRITE, 0); + + return 0; +diff --git a/drivers/pci/access.c b/drivers/pci/access.c +index b965c12168b7..502a82ca1db0 100644 +--- a/drivers/pci/access.c ++++ b/drivers/pci/access.c +@@ -442,7 +442,8 @@ static const struct pci_vpd_ops pci_vpd_pci22_ops = { + static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) + { +- struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); ++ struct pci_dev *tdev = pci_get_slot(dev->bus, ++ PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ssize_t ret; + + if (!tdev) +@@ -456,7 +457,8 @@ static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, + static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) + { +- struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); ++ struct pci_dev *tdev = pci_get_slot(dev->bus, ++ PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); + ssize_t ret; + + if (!tdev) +@@ -473,22 +475,6 @@ static const struct pci_vpd_ops pci_vpd_f0_ops = { + .release = pci_vpd_pci22_release, + }; + +-static int pci_vpd_f0_dev_check(struct pci_dev *dev) +-{ +- struct pci_dev *tdev = pci_get_slot(dev->bus, PCI_SLOT(dev->devfn)); +- int ret = 0; +- +- if (!tdev) +- return -ENODEV; +- if (!tdev->vpd || !tdev->multifunction || +- dev->class != tdev->class || dev->vendor != tdev->vendor || +- dev->device != tdev->device) +- ret = -ENODEV; +- +- pci_dev_put(tdev); +- return ret; +-} +- + int pci_vpd_pci22_init(struct pci_dev *dev) + { + struct pci_vpd_pci22 *vpd; +@@ -497,12 +483,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) + cap = pci_find_capability(dev, PCI_CAP_ID_VPD); + if (!cap) + return -ENODEV; +- if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) { +- int ret = pci_vpd_f0_dev_check(dev); + +- if (ret) +- return ret; +- } + vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC); + if (!vpd) + return -ENOMEM; +diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c +index 6fbd3f2b5992..d3346d23963b 100644 +--- a/drivers/pci/bus.c ++++ b/drivers/pci/bus.c +@@ -256,6 +256,8 @@ bool pci_bus_clip_resource(struct pci_dev *dev, int idx) + + res->start = start; + res->end = end; ++ res->flags &= ~IORESOURCE_UNSET; ++ orig_res.flags &= ~IORESOURCE_UNSET; + dev_printk(KERN_DEBUG, &dev->dev, "%pR clipped to %pR\n", + &orig_res, res); + +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index dbd13854f21e..6b1c6a915daa 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -1906,11 +1906,27 @@ static void quirk_netmos(struct pci_dev *dev) + DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, + PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos); + ++/* ++ * Quirk non-zero PCI functions to route VPD access through function 0 for ++ * devices that share VPD resources between functions. The functions are ++ * expected to be identical devices. ++ */ + static void quirk_f0_vpd_link(struct pci_dev *dev) + { +- if (!dev->multifunction || !PCI_FUNC(dev->devfn)) ++ struct pci_dev *f0; ++ ++ if (!PCI_FUNC(dev->devfn)) + return; +- dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; ++ ++ f0 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn), 0)); ++ if (!f0) ++ return; ++ ++ if (f0->vpd && dev->class == f0->class && ++ dev->vendor == f0->vendor && dev->device == f0->device) ++ dev->dev_flags |= PCI_DEV_FLAGS_VPD_REF_F0; ++ ++ pci_dev_put(f0); + } + DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, + PCI_CLASS_NETWORK_ETHERNET, 8, quirk_f0_vpd_link); +diff --git a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c +index 803945259da8..42861cc70158 100644 +--- a/drivers/pcmcia/sa1100_generic.c ++++ b/drivers/pcmcia/sa1100_generic.c +@@ -93,7 +93,6 @@ static int sa11x0_drv_pcmcia_remove(struct platform_device *dev) + for (i = 0; i < sinfo->nskt; i++) + soc_pcmcia_remove_one(&sinfo->skt[i]); + +- clk_put(sinfo->clk); + kfree(sinfo); + return 0; + } +diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c +index cf6de2c2b329..553d70a67f80 100644 +--- a/drivers/pcmcia/sa11xx_base.c ++++ b/drivers/pcmcia/sa11xx_base.c +@@ -222,7 +222,7 @@ int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, + int i, ret = 0; + struct clk *clk; + +- clk = clk_get(dev, NULL); ++ clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + +@@ -251,7 +251,6 @@ int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, + if (ret) { + while (--i >= 0) + soc_pcmcia_remove_one(&sinfo->skt[i]); +- clk_put(clk); + kfree(sinfo); + } else { + dev_set_drvdata(dev, sinfo); +diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c +index 3ad7b1fa24ce..6f4f310de946 100644 +--- a/drivers/platform/x86/toshiba_acpi.c ++++ b/drivers/platform/x86/toshiba_acpi.c +@@ -2408,11 +2408,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) + if (error) + return error; + +- error = toshiba_hotkey_event_type_get(dev, &events_type); +- if (error) { +- pr_err("Unable to query Hotkey Event Type\n"); +- return error; +- } ++ if (toshiba_hotkey_event_type_get(dev, &events_type)) ++ pr_notice("Unable to query Hotkey Event Type\n"); ++ + dev->hotkey_event_type = events_type; + + dev->hotkey_dev = input_allocate_device(); +diff --git a/drivers/power/avs/Kconfig b/drivers/power/avs/Kconfig +index 7f3d389bd601..a67eeace6a89 100644 +--- a/drivers/power/avs/Kconfig ++++ b/drivers/power/avs/Kconfig +@@ -13,7 +13,7 @@ menuconfig POWER_AVS + + config ROCKCHIP_IODOMAIN + tristate "Rockchip IO domain support" +- depends on ARCH_ROCKCHIP && OF ++ depends on POWER_AVS && ARCH_ROCKCHIP && OF + help + Say y here to enable support io domains on Rockchip SoCs. It is + necessary for the io domain setting of the SoC to match the +diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c +index 646829132b59..1dea0e8353e0 100644 +--- a/drivers/regulator/axp20x-regulator.c ++++ b/drivers/regulator/axp20x-regulator.c +@@ -192,9 +192,9 @@ static const struct regulator_desc axp22x_regulators[] = { + AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20, + AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), + AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20, +- AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)), ++ AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)), + AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50, +- AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(4)), ++ AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)), + /* secondary switchable output of DCDC1 */ + AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", "dcdc1", 1600, 3400, 100, + AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)), +diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c +index 78387a6cbae5..5081533858f1 100644 +--- a/drivers/regulator/core.c ++++ b/drivers/regulator/core.c +@@ -1376,15 +1376,19 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) + return 0; + + r = regulator_dev_lookup(dev, rdev->supply_name, &ret); +- if (ret == -ENODEV) { +- /* +- * No supply was specified for this regulator and +- * there will never be one. +- */ +- return 0; +- } +- + if (!r) { ++ if (ret == -ENODEV) { ++ /* ++ * No supply was specified for this regulator and ++ * there will never be one. ++ */ ++ return 0; ++ } ++ ++ /* Did the lookup explicitly defer for us? */ ++ if (ret == -EPROBE_DEFER) ++ return ret; ++ + if (have_full_constraints()) { + r = dummy_regulator_rdev; + } else { +diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c +index add419d6ff34..a56a7b243e91 100644 +--- a/drivers/scsi/3w-9xxx.c ++++ b/drivers/scsi/3w-9xxx.c +@@ -212,6 +212,17 @@ static const struct file_operations twa_fops = { + .llseek = noop_llseek, + }; + ++/* ++ * The controllers use an inline buffer instead of a mapped SGL for small, ++ * single entry buffers. Note that we treat a zero-length transfer like ++ * a mapped SGL. ++ */ ++static bool twa_command_mapped(struct scsi_cmnd *cmd) ++{ ++ return scsi_sg_count(cmd) != 1 || ++ scsi_bufflen(cmd) >= TW_MIN_SGL_LENGTH; ++} ++ + /* This function will complete an aen request from the isr */ + static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id) + { +@@ -1339,7 +1350,8 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) + } + + /* Now complete the io */ +- scsi_dma_unmap(cmd); ++ if (twa_command_mapped(cmd)) ++ scsi_dma_unmap(cmd); + cmd->scsi_done(cmd); + tw_dev->state[request_id] = TW_S_COMPLETED; + twa_free_request_id(tw_dev, request_id); +@@ -1582,7 +1594,8 @@ static int twa_reset_device_extension(TW_Device_Extension *tw_dev) + struct scsi_cmnd *cmd = tw_dev->srb[i]; + + cmd->result = (DID_RESET << 16); +- scsi_dma_unmap(cmd); ++ if (twa_command_mapped(cmd)) ++ scsi_dma_unmap(cmd); + cmd->scsi_done(cmd); + } + } +@@ -1765,12 +1778,14 @@ static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_ + retval = twa_scsiop_execute_scsi(tw_dev, request_id, NULL, 0, NULL); + switch (retval) { + case SCSI_MLQUEUE_HOST_BUSY: +- scsi_dma_unmap(SCpnt); ++ if (twa_command_mapped(SCpnt)) ++ scsi_dma_unmap(SCpnt); + twa_free_request_id(tw_dev, request_id); + break; + case 1: + SCpnt->result = (DID_ERROR << 16); +- scsi_dma_unmap(SCpnt); ++ if (twa_command_mapped(SCpnt)) ++ scsi_dma_unmap(SCpnt); + done(SCpnt); + tw_dev->state[request_id] = TW_S_COMPLETED; + twa_free_request_id(tw_dev, request_id); +@@ -1831,8 +1846,7 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, + /* Map sglist from scsi layer to cmd packet */ + + if (scsi_sg_count(srb)) { +- if ((scsi_sg_count(srb) == 1) && +- (scsi_bufflen(srb) < TW_MIN_SGL_LENGTH)) { ++ if (!twa_command_mapped(srb)) { + if (srb->sc_data_direction == DMA_TO_DEVICE || + srb->sc_data_direction == DMA_BIDIRECTIONAL) + scsi_sg_copy_to_buffer(srb, +@@ -1905,7 +1919,7 @@ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int re + { + struct scsi_cmnd *cmd = tw_dev->srb[request_id]; + +- if (scsi_bufflen(cmd) < TW_MIN_SGL_LENGTH && ++ if (!twa_command_mapped(cmd) && + (cmd->sc_data_direction == DMA_FROM_DEVICE || + cmd->sc_data_direction == DMA_BIDIRECTIONAL)) { + if (scsi_sg_count(cmd) == 1) { +diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c +index 1dafeb43333b..cab4e98b2b0e 100644 +--- a/drivers/scsi/hpsa.c ++++ b/drivers/scsi/hpsa.c +@@ -5104,7 +5104,7 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd) + int rc; + struct ctlr_info *h; + struct hpsa_scsi_dev_t *dev; +- char msg[40]; ++ char msg[48]; + + /* find the controller to which the command to be aborted was sent */ + h = sdev_to_hba(scsicmd->device); +@@ -5122,16 +5122,18 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd) + + /* if controller locked up, we can guarantee command won't complete */ + if (lockup_detected(h)) { +- sprintf(msg, "cmd %d RESET FAILED, lockup detected", +- hpsa_get_cmd_index(scsicmd)); ++ snprintf(msg, sizeof(msg), ++ "cmd %d RESET FAILED, lockup detected", ++ hpsa_get_cmd_index(scsicmd)); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + return FAILED; + } + + /* this reset request might be the result of a lockup; check */ + if (detect_controller_lockup(h)) { +- sprintf(msg, "cmd %d RESET FAILED, new lockup detected", +- hpsa_get_cmd_index(scsicmd)); ++ snprintf(msg, sizeof(msg), ++ "cmd %d RESET FAILED, new lockup detected", ++ hpsa_get_cmd_index(scsicmd)); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + return FAILED; + } +@@ -5145,7 +5147,8 @@ static int hpsa_eh_device_reset_handler(struct scsi_cmnd *scsicmd) + /* send a reset to the SCSI LUN which the command was sent to */ + rc = hpsa_do_reset(h, dev, dev->scsi3addr, HPSA_RESET_TYPE_LUN, + DEFAULT_REPLY_QUEUE); +- sprintf(msg, "reset %s", rc == 0 ? "completed successfully" : "failed"); ++ snprintf(msg, sizeof(msg), "reset %s", ++ rc == 0 ? "completed successfully" : "failed"); + hpsa_show_dev_msg(KERN_WARNING, h, dev, msg); + return rc == 0 ? SUCCESS : FAILED; + } +diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c +index a9aa38903efe..cccab6188328 100644 +--- a/drivers/scsi/ipr.c ++++ b/drivers/scsi/ipr.c +@@ -4554,7 +4554,7 @@ static ssize_t ipr_store_raw_mode(struct device *dev, + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = (struct ipr_resource_entry *)sdev->hostdata; + if (res) { +- if (ioa_cfg->sis64 && ipr_is_af_dasd_device(res)) { ++ if (ipr_is_af_dasd_device(res)) { + res->raw_mode = simple_strtoul(buf, NULL, 10); + len = strlen(buf); + if (res->sdev) +diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c +index 6457a8a0db9c..bf3d801ac5f9 100644 +--- a/drivers/scsi/scsi_error.c ++++ b/drivers/scsi/scsi_error.c +@@ -2169,8 +2169,17 @@ int scsi_error_handler(void *data) + * We never actually get interrupted because kthread_run + * disables signal delivery for the created thread. + */ +- while (!kthread_should_stop()) { ++ while (true) { ++ /* ++ * The sequence in kthread_stop() sets the stop flag first ++ * then wakes the process. To avoid missed wakeups, the task ++ * should always be in a non running state before the stop ++ * flag is checked ++ */ + set_current_state(TASK_INTERRUPTIBLE); ++ if (kthread_should_stop()) ++ break; ++ + if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || + shost->host_failed != atomic_read(&shost->host_busy)) { + SCSI_LOG_ERROR_RECOVERY(1, +diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c +index c9357bb393d3..744596464d33 100644 +--- a/drivers/spi/spi-bcm2835.c ++++ b/drivers/spi/spi-bcm2835.c +@@ -386,14 +386,14 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, + /* otherwise we only allow transfers within the same page + * to avoid wasting time on dma_mapping when it is not practical + */ +- if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { ++ if (((size_t)tfr->tx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { + dev_warn_once(&spi->dev, + "Unaligned spi tx-transfer bridging page\n"); + return false; + } +- if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { ++ if (((size_t)tfr->rx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { + dev_warn_once(&spi->dev, +- "Unaligned spi tx-transfer bridging page\n"); ++ "Unaligned spi rx-transfer bridging page\n"); + return false; + } + +diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c +index 7293d6d875c5..8e4b1a7c37ce 100644 +--- a/drivers/spi/spi-pxa2xx.c ++++ b/drivers/spi/spi-pxa2xx.c +@@ -643,6 +643,10 @@ static irqreturn_t ssp_int(int irq, void *dev_id) + if (!(sccr1_reg & SSCR1_TIE)) + mask &= ~SSSR_TFS; + ++ /* Ignore RX timeout interrupt if it is disabled */ ++ if (!(sccr1_reg & SSCR1_TINTE)) ++ mask &= ~SSSR_TINT; ++ + if (!(status & mask)) + return IRQ_NONE; + +diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c +index 2e32ea2f194f..be6155cba9de 100644 +--- a/drivers/spi/spi-xtensa-xtfpga.c ++++ b/drivers/spi/spi-xtensa-xtfpga.c +@@ -34,13 +34,13 @@ struct xtfpga_spi { + static inline void xtfpga_spi_write32(const struct xtfpga_spi *spi, + unsigned addr, u32 val) + { +- iowrite32(val, spi->regs + addr); ++ __raw_writel(val, spi->regs + addr); + } + + static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi, + unsigned addr) + { +- return ioread32(spi->regs + addr); ++ return __raw_readl(spi->regs + addr); + } + + static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi) +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index cf8b91b23a76..9ce2f156d382 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1437,8 +1437,7 @@ static struct class spi_master_class = { + * + * The caller is responsible for assigning the bus number and initializing + * the master's methods before calling spi_register_master(); and (after errors +- * adding the device) calling spi_master_put() and kfree() to prevent a memory +- * leak. ++ * adding the device) calling spi_master_put() to prevent a memory leak. + */ + struct spi_master *spi_alloc_master(struct device *dev, unsigned size) + { +diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c +index c7de64171c45..97aad8f91c2f 100644 +--- a/drivers/spi/spidev.c ++++ b/drivers/spi/spidev.c +@@ -651,7 +651,8 @@ static int spidev_release(struct inode *inode, struct file *filp) + kfree(spidev->rx_buffer); + spidev->rx_buffer = NULL; + +- spidev->speed_hz = spidev->spi->max_speed_hz; ++ if (spidev->spi) ++ spidev->speed_hz = spidev->spi->max_speed_hz; + + /* ... after we unbound from the underlying device? */ + spin_lock_irq(&spidev->spi_lock); +diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c +index 6f4811263557..b71b1f2d98d5 100644 +--- a/drivers/staging/android/ion/ion.c ++++ b/drivers/staging/android/ion/ion.c +@@ -1179,13 +1179,13 @@ struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) + mutex_unlock(&client->lock); + goto end; + } +- mutex_unlock(&client->lock); + + handle = ion_handle_create(client, buffer); +- if (IS_ERR(handle)) ++ if (IS_ERR(handle)) { ++ mutex_unlock(&client->lock); + goto end; ++ } + +- mutex_lock(&client->lock); + ret = ion_handle_add(client, handle); + mutex_unlock(&client->lock); + if (ret) { +diff --git a/drivers/staging/speakup/fakekey.c b/drivers/staging/speakup/fakekey.c +index 4299cf45f947..5e1f16c36b49 100644 +--- a/drivers/staging/speakup/fakekey.c ++++ b/drivers/staging/speakup/fakekey.c +@@ -81,6 +81,7 @@ void speakup_fake_down_arrow(void) + __this_cpu_write(reporting_keystroke, true); + input_report_key(virt_keyboard, KEY_DOWN, PRESSED); + input_report_key(virt_keyboard, KEY_DOWN, RELEASED); ++ input_sync(virt_keyboard); + __this_cpu_write(reporting_keystroke, false); + + /* reenable preemption */ +diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c +index fd092909a457..56cf1996f30f 100644 +--- a/drivers/target/iscsi/iscsi_target.c ++++ b/drivers/target/iscsi/iscsi_target.c +@@ -341,7 +341,6 @@ static struct iscsi_np *iscsit_get_np( + + struct iscsi_np *iscsit_add_np( + struct __kernel_sockaddr_storage *sockaddr, +- char *ip_str, + int network_transport) + { + struct sockaddr_in *sock_in; +@@ -370,11 +369,9 @@ struct iscsi_np *iscsit_add_np( + np->np_flags |= NPF_IP_NETWORK; + if (sockaddr->ss_family == AF_INET6) { + sock_in6 = (struct sockaddr_in6 *)sockaddr; +- snprintf(np->np_ip, IPV6_ADDRESS_SPACE, "%s", ip_str); + np->np_port = ntohs(sock_in6->sin6_port); + } else { + sock_in = (struct sockaddr_in *)sockaddr; +- sprintf(np->np_ip, "%s", ip_str); + np->np_port = ntohs(sock_in->sin_port); + } + +@@ -411,8 +408,8 @@ struct iscsi_np *iscsit_add_np( + list_add_tail(&np->np_list, &g_np_list); + mutex_unlock(&np_lock); + +- pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", +- np->np_ip, np->np_port, np->np_transport->name); ++ pr_debug("CORE[0] - Added Network Portal: %pISc:%hu on %s\n", ++ &np->np_sockaddr, np->np_port, np->np_transport->name); + + return np; + } +@@ -481,8 +478,8 @@ int iscsit_del_np(struct iscsi_np *np) + list_del(&np->np_list); + mutex_unlock(&np_lock); + +- pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", +- np->np_ip, np->np_port, np->np_transport->name); ++ pr_debug("CORE[0] - Removed Network Portal: %pISc:%hu on %s\n", ++ &np->np_sockaddr, np->np_port, np->np_transport->name); + + iscsit_put_transport(np->np_transport); + kfree(np); +@@ -3464,7 +3461,6 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, + tpg_np_list) { + struct iscsi_np *np = tpg_np->tpg_np; + bool inaddr_any = iscsit_check_inaddr_any(np); +- char *fmt_str; + + if (np->np_network_transport != network_transport) + continue; +@@ -3492,15 +3488,18 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, + } + } + +- if (np->np_sockaddr.ss_family == AF_INET6) +- fmt_str = "TargetAddress=[%s]:%hu,%hu"; +- else +- fmt_str = "TargetAddress=%s:%hu,%hu"; +- +- len = sprintf(buf, fmt_str, +- inaddr_any ? conn->local_ip : np->np_ip, +- np->np_port, +- tpg->tpgt); ++ if (inaddr_any) { ++ len = sprintf(buf, "TargetAddress=" ++ "%s:%hu,%hu", ++ conn->local_ip, ++ np->np_port, ++ tpg->tpgt); ++ } else { ++ len = sprintf(buf, "TargetAddress=" ++ "%pISpc,%hu", ++ &np->np_sockaddr, ++ tpg->tpgt); ++ } + len += 1; + + if ((len + payload_len) > buffer_len) { +diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h +index 7d0f9c00d9c2..d294f030a097 100644 +--- a/drivers/target/iscsi/iscsi_target.h ++++ b/drivers/target/iscsi/iscsi_target.h +@@ -13,7 +13,7 @@ extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *, + extern bool iscsit_check_np_match(struct __kernel_sockaddr_storage *, + struct iscsi_np *, int); + extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *, +- char *, int); ++ int); + extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *, + struct iscsi_portal_group *, bool); + extern int iscsit_del_np(struct iscsi_np *); +diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c +index c1898c84b3d2..db3b9b986954 100644 +--- a/drivers/target/iscsi/iscsi_target_configfs.c ++++ b/drivers/target/iscsi/iscsi_target_configfs.c +@@ -99,7 +99,7 @@ static ssize_t lio_target_np_store_sctp( + * Use existing np->np_sockaddr for SCTP network portal reference + */ + tpg_np_sctp = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr, +- np->np_ip, tpg_np, ISCSI_SCTP_TCP); ++ tpg_np, ISCSI_SCTP_TCP); + if (!tpg_np_sctp || IS_ERR(tpg_np_sctp)) + goto out; + } else { +@@ -177,7 +177,7 @@ static ssize_t lio_target_np_store_iser( + } + + tpg_np_iser = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr, +- np->np_ip, tpg_np, ISCSI_INFINIBAND); ++ tpg_np, ISCSI_INFINIBAND); + if (IS_ERR(tpg_np_iser)) { + rc = PTR_ERR(tpg_np_iser); + goto out; +@@ -248,8 +248,8 @@ static struct se_tpg_np *lio_target_call_addnptotpg( + return ERR_PTR(-EINVAL); + } + str++; /* Skip over leading "[" */ +- *str2 = '\0'; /* Terminate the IPv6 address */ +- str2++; /* Skip over the "]" */ ++ *str2 = '\0'; /* Terminate the unbracketed IPv6 address */ ++ str2++; /* Skip over the \0 */ + port_str = strstr(str2, ":"); + if (!port_str) { + pr_err("Unable to locate \":port\"" +@@ -316,7 +316,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg( + * sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/ + * + */ +- tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, str, NULL, ++ tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, NULL, + ISCSI_TCP); + if (IS_ERR(tpg_np)) { + iscsit_put_tpg(tpg); +@@ -344,8 +344,8 @@ static void lio_target_call_delnpfromtpg( + + se_tpg = &tpg->tpg_se_tpg; + pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s TPGT: %hu" +- " PORTAL: %s:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item), +- tpg->tpgt, tpg_np->tpg_np->np_ip, tpg_np->tpg_np->np_port); ++ " PORTAL: %pISc:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item), ++ tpg->tpgt, &tpg_np->tpg_np->np_sockaddr, tpg_np->tpg_np->np_port); + + ret = iscsit_tpg_del_network_portal(tpg, tpg_np); + if (ret < 0) +diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c +index 7e8f65e5448f..666c0739bfbe 100644 +--- a/drivers/target/iscsi/iscsi_target_login.c ++++ b/drivers/target/iscsi/iscsi_target_login.c +@@ -823,8 +823,8 @@ static void iscsi_handle_login_thread_timeout(unsigned long data) + struct iscsi_np *np = (struct iscsi_np *) data; + + spin_lock_bh(&np->np_thread_lock); +- pr_err("iSCSI Login timeout on Network Portal %s:%hu\n", +- np->np_ip, np->np_port); ++ pr_err("iSCSI Login timeout on Network Portal %pISc:%hu\n", ++ &np->np_sockaddr, np->np_port); + + if (np->np_login_timer_flags & ISCSI_TF_STOP) { + spin_unlock_bh(&np->np_thread_lock); +@@ -1302,8 +1302,8 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) + spin_lock_bh(&np->np_thread_lock); + if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { + spin_unlock_bh(&np->np_thread_lock); +- pr_err("iSCSI Network Portal on %s:%hu currently not" +- " active.\n", np->np_ip, np->np_port); ++ pr_err("iSCSI Network Portal on %pISc:%hu currently not" ++ " active.\n", &np->np_sockaddr, np->np_port); + iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, + ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); + goto new_sess_out; +diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c +index e8a52f7d6204..51d1734d5390 100644 +--- a/drivers/target/iscsi/iscsi_target_parameters.c ++++ b/drivers/target/iscsi/iscsi_target_parameters.c +@@ -407,6 +407,7 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr) + TYPERANGE_UTF8, USE_INITIAL_ONLY); + if (!param) + goto out; ++ + /* + * Extra parameters for ISER from RFC-5046 + */ +@@ -496,9 +497,9 @@ int iscsi_set_keys_to_negotiate( + } else if (!strcmp(param->name, SESSIONTYPE)) { + SET_PSTATE_NEGOTIATE(param); + } else if (!strcmp(param->name, IFMARKER)) { +- SET_PSTATE_NEGOTIATE(param); ++ SET_PSTATE_REJECT(param); + } else if (!strcmp(param->name, OFMARKER)) { +- SET_PSTATE_NEGOTIATE(param); ++ SET_PSTATE_REJECT(param); + } else if (!strcmp(param->name, IFMARKINT)) { + SET_PSTATE_REJECT(param); + } else if (!strcmp(param->name, OFMARKINT)) { +diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c +index 968068ffcb1c..de26bee4bddd 100644 +--- a/drivers/target/iscsi/iscsi_target_tpg.c ++++ b/drivers/target/iscsi/iscsi_target_tpg.c +@@ -460,7 +460,6 @@ static bool iscsit_tpg_check_network_portal( + struct iscsi_tpg_np *iscsit_tpg_add_network_portal( + struct iscsi_portal_group *tpg, + struct __kernel_sockaddr_storage *sockaddr, +- char *ip_str, + struct iscsi_tpg_np *tpg_np_parent, + int network_transport) + { +@@ -470,8 +469,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( + if (!tpg_np_parent) { + if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr, + network_transport)) { +- pr_err("Network Portal: %s already exists on a" +- " different TPG on %s\n", ip_str, ++ pr_err("Network Portal: %pISc already exists on a" ++ " different TPG on %s\n", sockaddr, + tpg->tpg_tiqn->tiqn); + return ERR_PTR(-EEXIST); + } +@@ -484,7 +483,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( + return ERR_PTR(-ENOMEM); + } + +- np = iscsit_add_np(sockaddr, ip_str, network_transport); ++ np = iscsit_add_np(sockaddr, network_transport); + if (IS_ERR(np)) { + kfree(tpg_np); + return ERR_CAST(np); +@@ -514,8 +513,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( + spin_unlock(&tpg_np_parent->tpg_np_parent_lock); + } + +- pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n", +- tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, ++ pr_debug("CORE[%s] - Added Network Portal: %pISc:%hu,%hu on %s\n", ++ tpg->tpg_tiqn->tiqn, &np->np_sockaddr, np->np_port, tpg->tpgt, + np->np_transport->name); + + return tpg_np; +@@ -528,8 +527,8 @@ static int iscsit_tpg_release_np( + { + iscsit_clear_tpg_np_login_thread(tpg_np, tpg, true); + +- pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n", +- tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, ++ pr_debug("CORE[%s] - Removed Network Portal: %pISc:%hu,%hu on %s\n", ++ tpg->tpg_tiqn->tiqn, &np->np_sockaddr, np->np_port, tpg->tpgt, + np->np_transport->name); + + tpg_np->tpg_np = NULL; +diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h +index 95ff5bdecd71..28abda89ea98 100644 +--- a/drivers/target/iscsi/iscsi_target_tpg.h ++++ b/drivers/target/iscsi/iscsi_target_tpg.h +@@ -22,7 +22,7 @@ extern struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(struct iscsi_session + extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *); + extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int); + extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *, +- struct __kernel_sockaddr_storage *, char *, struct iscsi_tpg_np *, ++ struct __kernel_sockaddr_storage *, struct iscsi_tpg_np *, + int); + extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *, + struct iscsi_tpg_np *); +diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c +index 09e682b1c549..8f1cd194f06a 100644 +--- a/drivers/target/target_core_device.c ++++ b/drivers/target/target_core_device.c +@@ -427,8 +427,6 @@ void core_disable_device_list_for_node( + + hlist_del_rcu(&orig->link); + clear_bit(DEF_PR_REG_ACTIVE, &orig->deve_flags); +- rcu_assign_pointer(orig->se_lun, NULL); +- rcu_assign_pointer(orig->se_lun_acl, NULL); + orig->lun_flags = 0; + orig->creation_time = 0; + orig->attach_count--; +@@ -439,6 +437,9 @@ void core_disable_device_list_for_node( + kref_put(&orig->pr_kref, target_pr_kref_release); + wait_for_completion(&orig->pr_comp); + ++ rcu_assign_pointer(orig->se_lun, NULL); ++ rcu_assign_pointer(orig->se_lun_acl, NULL); ++ + kfree_rcu(orig, rcu_head); + + core_scsi3_free_pr_reg_from_nacl(dev, nacl); +diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c +index 5ab7100de17e..e7933115087a 100644 +--- a/drivers/target/target_core_pr.c ++++ b/drivers/target/target_core_pr.c +@@ -618,7 +618,7 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration( + struct se_device *dev, + struct se_node_acl *nacl, + struct se_lun *lun, +- struct se_dev_entry *deve, ++ struct se_dev_entry *dest_deve, + u64 mapped_lun, + unsigned char *isid, + u64 sa_res_key, +@@ -640,7 +640,29 @@ static struct t10_pr_registration *__core_scsi3_do_alloc_registration( + INIT_LIST_HEAD(&pr_reg->pr_reg_atp_mem_list); + atomic_set(&pr_reg->pr_res_holders, 0); + pr_reg->pr_reg_nacl = nacl; +- pr_reg->pr_reg_deve = deve; ++ /* ++ * For destination registrations for ALL_TG_PT=1 and SPEC_I_PT=1, ++ * the se_dev_entry->pr_ref will have been already obtained by ++ * core_get_se_deve_from_rtpi() or __core_scsi3_alloc_registration(). ++ * ++ * Otherwise, locate se_dev_entry now and obtain a reference until ++ * registration completes in __core_scsi3_add_registration(). ++ */ ++ if (dest_deve) { ++ pr_reg->pr_reg_deve = dest_deve; ++ } else { ++ rcu_read_lock(); ++ pr_reg->pr_reg_deve = target_nacl_find_deve(nacl, mapped_lun); ++ if (!pr_reg->pr_reg_deve) { ++ rcu_read_unlock(); ++ pr_err("Unable to locate PR deve %s mapped_lun: %llu\n", ++ nacl->initiatorname, mapped_lun); ++ kmem_cache_free(t10_pr_reg_cache, pr_reg); ++ return NULL; ++ } ++ kref_get(&pr_reg->pr_reg_deve->pr_kref); ++ rcu_read_unlock(); ++ } + pr_reg->pr_res_mapped_lun = mapped_lun; + pr_reg->pr_aptpl_target_lun = lun->unpacked_lun; + pr_reg->tg_pt_sep_rtpi = lun->lun_rtpi; +@@ -936,17 +958,29 @@ static int __core_scsi3_check_aptpl_registration( + !(strcmp(pr_reg->pr_tport, t_port)) && + (pr_reg->pr_reg_tpgt == tpgt) && + (pr_reg->pr_aptpl_target_lun == target_lun)) { ++ /* ++ * Obtain the ->pr_reg_deve pointer + reference, that ++ * is released by __core_scsi3_add_registration() below. ++ */ ++ rcu_read_lock(); ++ pr_reg->pr_reg_deve = target_nacl_find_deve(nacl, mapped_lun); ++ if (!pr_reg->pr_reg_deve) { ++ pr_err("Unable to locate PR APTPL %s mapped_lun:" ++ " %llu\n", nacl->initiatorname, mapped_lun); ++ rcu_read_unlock(); ++ continue; ++ } ++ kref_get(&pr_reg->pr_reg_deve->pr_kref); ++ rcu_read_unlock(); + + pr_reg->pr_reg_nacl = nacl; + pr_reg->tg_pt_sep_rtpi = lun->lun_rtpi; +- + list_del(&pr_reg->pr_reg_aptpl_list); + spin_unlock(&pr_tmpl->aptpl_reg_lock); + /* + * At this point all of the pointers in *pr_reg will + * be setup, so go ahead and add the registration. + */ +- + __core_scsi3_add_registration(dev, nacl, pr_reg, 0, 0); + /* + * If this registration is the reservation holder, +@@ -1044,18 +1078,11 @@ static void __core_scsi3_add_registration( + + __core_scsi3_dump_registration(tfo, dev, nacl, pr_reg, register_type); + spin_unlock(&pr_tmpl->registration_lock); +- +- rcu_read_lock(); +- deve = pr_reg->pr_reg_deve; +- if (deve) +- set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); +- rcu_read_unlock(); +- + /* + * Skip extra processing for ALL_TG_PT=0 or REGISTER_AND_MOVE. + */ + if (!pr_reg->pr_reg_all_tg_pt || register_move) +- return; ++ goto out; + /* + * Walk pr_reg->pr_reg_atp_list and add registrations for ALL_TG_PT=1 + * allocated in __core_scsi3_alloc_registration() +@@ -1075,19 +1102,31 @@ static void __core_scsi3_add_registration( + __core_scsi3_dump_registration(tfo, dev, nacl_tmp, pr_reg_tmp, + register_type); + spin_unlock(&pr_tmpl->registration_lock); +- ++ /* ++ * Drop configfs group dependency reference and deve->pr_kref ++ * obtained from __core_scsi3_alloc_registration() code. ++ */ + rcu_read_lock(); + deve = pr_reg_tmp->pr_reg_deve; +- if (deve) ++ if (deve) { + set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); ++ core_scsi3_lunacl_undepend_item(deve); ++ pr_reg_tmp->pr_reg_deve = NULL; ++ } + rcu_read_unlock(); +- +- /* +- * Drop configfs group dependency reference from +- * __core_scsi3_alloc_registration() +- */ +- core_scsi3_lunacl_undepend_item(pr_reg_tmp->pr_reg_deve); + } ++out: ++ /* ++ * Drop deve->pr_kref obtained in __core_scsi3_do_alloc_registration() ++ */ ++ rcu_read_lock(); ++ deve = pr_reg->pr_reg_deve; ++ if (deve) { ++ set_bit(DEF_PR_REG_ACTIVE, &deve->deve_flags); ++ kref_put(&deve->pr_kref, target_pr_kref_release); ++ pr_reg->pr_reg_deve = NULL; ++ } ++ rcu_read_unlock(); + } + + static int core_scsi3_alloc_registration( +@@ -1785,9 +1824,11 @@ core_scsi3_decode_spec_i_port( + dest_node_acl->initiatorname, i_buf, (dest_se_deve) ? + dest_se_deve->mapped_lun : 0); + +- if (!dest_se_deve) ++ if (!dest_se_deve) { ++ kref_put(&local_pr_reg->pr_reg_deve->pr_kref, ++ target_pr_kref_release); + continue; +- ++ } + core_scsi3_lunacl_undepend_item(dest_se_deve); + core_scsi3_nodeacl_undepend_item(dest_node_acl); + core_scsi3_tpg_undepend_item(dest_tpg); +@@ -1823,9 +1864,11 @@ out: + + kmem_cache_free(t10_pr_reg_cache, dest_pr_reg); + +- if (!dest_se_deve) ++ if (!dest_se_deve) { ++ kref_put(&local_pr_reg->pr_reg_deve->pr_kref, ++ target_pr_kref_release); + continue; +- ++ } + core_scsi3_lunacl_undepend_item(dest_se_deve); + core_scsi3_nodeacl_undepend_item(dest_node_acl); + core_scsi3_tpg_undepend_item(dest_tpg); +diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c +index 4515f52546f8..47fe94ee10b8 100644 +--- a/drivers/target/target_core_xcopy.c ++++ b/drivers/target/target_core_xcopy.c +@@ -450,6 +450,8 @@ int target_xcopy_setup_pt(void) + memset(&xcopy_pt_sess, 0, sizeof(struct se_session)); + INIT_LIST_HEAD(&xcopy_pt_sess.sess_list); + INIT_LIST_HEAD(&xcopy_pt_sess.sess_acl_list); ++ INIT_LIST_HEAD(&xcopy_pt_sess.sess_cmd_list); ++ spin_lock_init(&xcopy_pt_sess.sess_cmd_lock); + + xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg; + xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess; +@@ -644,7 +646,7 @@ static int target_xcopy_read_source( + pr_debug("XCOPY: Built READ_16: LBA: %llu Sectors: %u Length: %u\n", + (unsigned long long)src_lba, src_sectors, length); + +- transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length, ++ transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, + DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]); + xop->src_pt_cmd = xpt_cmd; + +@@ -704,7 +706,7 @@ static int target_xcopy_write_destination( + pr_debug("XCOPY: Built WRITE_16: LBA: %llu Sectors: %u Length: %u\n", + (unsigned long long)dst_lba, dst_sectors, length); + +- transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length, ++ transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length, + DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]); + xop->dst_pt_cmd = xpt_cmd; + +diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c +index 620dcd405ff6..42c6f71bdcc1 100644 +--- a/drivers/thermal/cpu_cooling.c ++++ b/drivers/thermal/cpu_cooling.c +@@ -262,7 +262,9 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, + * efficiently. Power is stored in mW, frequency in KHz. The + * resulting table is in ascending order. + * +- * Return: 0 on success, -E* on error. ++ * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, ++ * -ENOMEM if we run out of memory or -EAGAIN if an OPP was ++ * added/enabled while the function was executing. + */ + static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, + u32 capacitance) +@@ -273,8 +275,6 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, + int num_opps = 0, cpu, i, ret = 0; + unsigned long freq; + +- rcu_read_lock(); +- + for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { + dev = get_cpu_device(cpu); + if (!dev) { +@@ -284,24 +284,20 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, + } + + num_opps = dev_pm_opp_get_opp_count(dev); +- if (num_opps > 0) { ++ if (num_opps > 0) + break; +- } else if (num_opps < 0) { +- ret = num_opps; +- goto unlock; +- } ++ else if (num_opps < 0) ++ return num_opps; + } + +- if (num_opps == 0) { +- ret = -EINVAL; +- goto unlock; +- } ++ if (num_opps == 0) ++ return -EINVAL; + + power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); +- if (!power_table) { +- ret = -ENOMEM; +- goto unlock; +- } ++ if (!power_table) ++ return -ENOMEM; ++ ++ rcu_read_lock(); + + for (freq = 0, i = 0; + opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); +@@ -309,6 +305,12 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, + u32 freq_mhz, voltage_mv; + u64 power; + ++ if (i >= num_opps) { ++ rcu_read_unlock(); ++ ret = -EAGAIN; ++ goto free_power_table; ++ } ++ + freq_mhz = freq / 1000000; + voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; + +@@ -326,17 +328,22 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, + power_table[i].power = power; + } + +- if (i == 0) { ++ rcu_read_unlock(); ++ ++ if (i != num_opps) { + ret = PTR_ERR(opp); +- goto unlock; ++ goto free_power_table; + } + + cpufreq_device->cpu_dev = dev; + cpufreq_device->dyn_power_table = power_table; + cpufreq_device->dyn_power_table_entries = i; + +-unlock: +- rcu_read_unlock(); ++ return 0; ++ ++free_power_table: ++ kfree(power_table); ++ + return ret; + } + +@@ -847,7 +854,7 @@ __cpufreq_cooling_register(struct device_node *np, + ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); + if (ret) { + cool_dev = ERR_PTR(ret); +- goto free_table; ++ goto free_power_table; + } + + snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d", +@@ -889,6 +896,8 @@ __cpufreq_cooling_register(struct device_node *np, + + remove_idr: + release_idr(&cpufreq_idr, cpufreq_dev->id); ++free_power_table: ++ kfree(cpufreq_dev->dyn_power_table); + free_table: + kfree(cpufreq_dev->freq_table); + free_time_in_idle_timestamp: +@@ -1039,6 +1048,7 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) + + thermal_cooling_device_unregister(cpufreq_dev->cool_dev); + release_idr(&cpufreq_idr, cpufreq_dev->id); ++ kfree(cpufreq_dev->dyn_power_table); + kfree(cpufreq_dev->time_in_idle_timestamp); + kfree(cpufreq_dev->time_in_idle); + kfree(cpufreq_dev->freq_table); +diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c +index ee8bfacf2071..afc1879f66e0 100644 +--- a/drivers/tty/n_tty.c ++++ b/drivers/tty/n_tty.c +@@ -343,8 +343,7 @@ static void n_tty_packet_mode_flush(struct tty_struct *tty) + spin_lock_irqsave(&tty->ctrl_lock, flags); + tty->ctrl_status |= TIOCPKT_FLUSHREAD; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); +- if (waitqueue_active(&tty->link->read_wait)) +- wake_up_interruptible(&tty->link->read_wait); ++ wake_up_interruptible(&tty->link->read_wait); + } + } + +@@ -1382,8 +1381,7 @@ handle_newline: + put_tty_queue(c, ldata); + smp_store_release(&ldata->canon_head, ldata->read_head); + kill_fasync(&tty->fasync, SIGIO, POLL_IN); +- if (waitqueue_active(&tty->read_wait)) +- wake_up_interruptible_poll(&tty->read_wait, POLLIN); ++ wake_up_interruptible_poll(&tty->read_wait, POLLIN); + return 0; + } + } +@@ -1667,8 +1665,7 @@ static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + + if ((read_cnt(ldata) >= ldata->minimum_to_wake) || L_EXTPROC(tty)) { + kill_fasync(&tty->fasync, SIGIO, POLL_IN); +- if (waitqueue_active(&tty->read_wait)) +- wake_up_interruptible_poll(&tty->read_wait, POLLIN); ++ wake_up_interruptible_poll(&tty->read_wait, POLLIN); + } + } + +@@ -1887,10 +1884,8 @@ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) + } + + /* The termios change make the tty ready for I/O */ +- if (waitqueue_active(&tty->write_wait)) +- wake_up_interruptible(&tty->write_wait); +- if (waitqueue_active(&tty->read_wait)) +- wake_up_interruptible(&tty->read_wait); ++ wake_up_interruptible(&tty->write_wait); ++ wake_up_interruptible(&tty->read_wait); + } + + /** +diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c +index 37fff12dd4d0..c35d96ece8ff 100644 +--- a/drivers/tty/serial/8250/8250_core.c ++++ b/drivers/tty/serial/8250/8250_core.c +@@ -326,6 +326,14 @@ configured less than Maximum supported fifo bytes */ + UART_FCR7_64BYTE, + .flags = UART_CAP_FIFO, + }, ++ [PORT_RT2880] = { ++ .name = "Palmchip BK-3103", ++ .fifo_size = 16, ++ .tx_loadsz = 16, ++ .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, ++ .rxtrig_bytes = {1, 4, 8, 14}, ++ .flags = UART_CAP_FIFO, ++ }, + }; + + /* Uart divisor latch read */ +diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c +index 2a8f528153e7..40326b342762 100644 +--- a/drivers/tty/serial/atmel_serial.c ++++ b/drivers/tty/serial/atmel_serial.c +@@ -2641,7 +2641,7 @@ static int atmel_serial_probe(struct platform_device *pdev) + ret = atmel_init_gpios(port, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to initialize GPIOs."); +- goto err; ++ goto err_clear_bit; + } + + ret = atmel_init_port(port, pdev); +diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c +index 57fc6ee12332..774df354af55 100644 +--- a/drivers/tty/tty_io.c ++++ b/drivers/tty/tty_io.c +@@ -2136,8 +2136,24 @@ retry_open: + if (!noctty && + current->signal->leader && + !current->signal->tty && +- tty->session == NULL) +- __proc_set_tty(tty); ++ tty->session == NULL) { ++ /* ++ * Don't let a process that only has write access to the tty ++ * obtain the privileges associated with having a tty as ++ * controlling terminal (being able to reopen it with full ++ * access through /dev/tty, being able to perform pushback). ++ * Many distributions set the group of all ttys to "tty" and ++ * grant write-only access to all terminals for setgid tty ++ * binaries, which should not imply full privileges on all ttys. ++ * ++ * This could theoretically break old code that performs open() ++ * on a write-only file descriptor. In that case, it might be ++ * necessary to also permit this if ++ * inode_permission(inode, MAY_READ) == 0. ++ */ ++ if (filp->f_mode & FMODE_READ) ++ __proc_set_tty(tty); ++ } + spin_unlock_irq(¤t->sighand->siglock); + read_unlock(&tasklist_lock); + tty_unlock(tty); +@@ -2426,7 +2442,7 @@ static int fionbio(struct file *file, int __user *p) + * Takes ->siglock() when updating signal->tty + */ + +-static int tiocsctty(struct tty_struct *tty, int arg) ++static int tiocsctty(struct tty_struct *tty, struct file *file, int arg) + { + int ret = 0; + +@@ -2460,6 +2476,13 @@ static int tiocsctty(struct tty_struct *tty, int arg) + goto unlock; + } + } ++ ++ /* See the comment in tty_open(). */ ++ if ((file->f_mode & FMODE_READ) == 0 && !capable(CAP_SYS_ADMIN)) { ++ ret = -EPERM; ++ goto unlock; ++ } ++ + proc_set_tty(tty); + unlock: + read_unlock(&tasklist_lock); +@@ -2852,7 +2875,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) + no_tty(); + return 0; + case TIOCSCTTY: +- return tiocsctty(tty, arg); ++ return tiocsctty(tty, file, arg); + case TIOCGPGRP: + return tiocgpgrp(tty, real_tty, p); + case TIOCSPGRP: +diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c +index 389f0e034259..fa774323ebda 100644 +--- a/drivers/usb/chipidea/ci_hdrc_imx.c ++++ b/drivers/usb/chipidea/ci_hdrc_imx.c +@@ -56,7 +56,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = { + { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, + { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, + { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, +- { .compatible = "fsl,imx6sx-usb", .data = &imx6sl_usb_data}, ++ { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); +diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c +index 764f668d45a9..6e53c24fa1cb 100644 +--- a/drivers/usb/chipidea/udc.c ++++ b/drivers/usb/chipidea/udc.c +@@ -656,6 +656,44 @@ __acquires(hwep->lock) + return 0; + } + ++static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer) ++{ ++ struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); ++ int direction, retval = 0; ++ unsigned long flags; ++ ++ if (ep == NULL || hwep->ep.desc == NULL) ++ return -EINVAL; ++ ++ if (usb_endpoint_xfer_isoc(hwep->ep.desc)) ++ return -EOPNOTSUPP; ++ ++ spin_lock_irqsave(hwep->lock, flags); ++ ++ if (value && hwep->dir == TX && check_transfer && ++ !list_empty(&hwep->qh.queue) && ++ !usb_endpoint_xfer_control(hwep->ep.desc)) { ++ spin_unlock_irqrestore(hwep->lock, flags); ++ return -EAGAIN; ++ } ++ ++ direction = hwep->dir; ++ do { ++ retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); ++ ++ if (!value) ++ hwep->wedge = 0; ++ ++ if (hwep->type == USB_ENDPOINT_XFER_CONTROL) ++ hwep->dir = (hwep->dir == TX) ? RX : TX; ++ ++ } while (hwep->dir != direction); ++ ++ spin_unlock_irqrestore(hwep->lock, flags); ++ return retval; ++} ++ ++ + /** + * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts + * @gadget: gadget +@@ -1051,7 +1089,7 @@ __acquires(ci->lock) + num += ci->hw_ep_max / 2; + + spin_unlock(&ci->lock); +- err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep); ++ err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false); + spin_lock(&ci->lock); + if (!err) + isr_setup_status_phase(ci); +@@ -1110,8 +1148,8 @@ delegate: + + if (err < 0) { + spin_unlock(&ci->lock); +- if (usb_ep_set_halt(&hwep->ep)) +- dev_err(ci->dev, "error: ep_set_halt\n"); ++ if (_ep_set_halt(&hwep->ep, 1, false)) ++ dev_err(ci->dev, "error: _ep_set_halt\n"); + spin_lock(&ci->lock); + } + } +@@ -1142,9 +1180,9 @@ __acquires(ci->lock) + err = isr_setup_status_phase(ci); + if (err < 0) { + spin_unlock(&ci->lock); +- if (usb_ep_set_halt(&hwep->ep)) ++ if (_ep_set_halt(&hwep->ep, 1, false)) + dev_err(ci->dev, +- "error: ep_set_halt\n"); ++ "error: _ep_set_halt\n"); + spin_lock(&ci->lock); + } + } +@@ -1390,41 +1428,7 @@ static int ep_dequeue(struct usb_ep *ep, struct usb_request *req) + */ + static int ep_set_halt(struct usb_ep *ep, int value) + { +- struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep); +- int direction, retval = 0; +- unsigned long flags; +- +- if (ep == NULL || hwep->ep.desc == NULL) +- return -EINVAL; +- +- if (usb_endpoint_xfer_isoc(hwep->ep.desc)) +- return -EOPNOTSUPP; +- +- spin_lock_irqsave(hwep->lock, flags); +- +-#ifndef STALL_IN +- /* g_file_storage MS compliant but g_zero fails chapter 9 compliance */ +- if (value && hwep->type == USB_ENDPOINT_XFER_BULK && hwep->dir == TX && +- !list_empty(&hwep->qh.queue)) { +- spin_unlock_irqrestore(hwep->lock, flags); +- return -EAGAIN; +- } +-#endif +- +- direction = hwep->dir; +- do { +- retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value); +- +- if (!value) +- hwep->wedge = 0; +- +- if (hwep->type == USB_ENDPOINT_XFER_CONTROL) +- hwep->dir = (hwep->dir == TX) ? RX : TX; +- +- } while (hwep->dir != direction); +- +- spin_unlock_irqrestore(hwep->lock, flags); +- return retval; ++ return _ep_set_halt(ep, value, true); + } + + /** +diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c +index b2a540b43f97..b9ddf0c1ffe5 100644 +--- a/drivers/usb/core/config.c ++++ b/drivers/usb/core/config.c +@@ -112,7 +112,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, + cfgno, inum, asnum, ep->desc.bEndpointAddress); + ep->ss_ep_comp.bmAttributes = 16; + } else if (usb_endpoint_xfer_isoc(&ep->desc) && +- desc->bmAttributes > 2) { ++ USB_SS_MULT(desc->bmAttributes) > 3) { + dev_warn(ddev, "Isoc endpoint has Mult of %d in " + "config %d interface %d altsetting %d ep %d: " + "setting to 3\n", desc->bmAttributes + 1, +@@ -121,7 +121,8 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, + } + + if (usb_endpoint_xfer_isoc(&ep->desc)) +- max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) * ++ max_tx = (desc->bMaxBurst + 1) * ++ (USB_SS_MULT(desc->bmAttributes)) * + usb_endpoint_maxp(&ep->desc); + else if (usb_endpoint_xfer_int(&ep->desc)) + max_tx = usb_endpoint_maxp(&ep->desc) * +diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c +index d85abfed84cc..f5a381945db2 100644 +--- a/drivers/usb/core/quirks.c ++++ b/drivers/usb/core/quirks.c +@@ -54,6 +54,13 @@ static const struct usb_device_id usb_quirk_list[] = { + { USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT }, + { USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT }, + ++ /* Logitech ConferenceCam CC3000e */ ++ { USB_DEVICE(0x046d, 0x0847), .driver_info = USB_QUIRK_DELAY_INIT }, ++ { USB_DEVICE(0x046d, 0x0848), .driver_info = USB_QUIRK_DELAY_INIT }, ++ ++ /* Logitech PTZ Pro Camera */ ++ { USB_DEVICE(0x046d, 0x0853), .driver_info = USB_QUIRK_DELAY_INIT }, ++ + /* Logitech Quickcam Fusion */ + { USB_DEVICE(0x046d, 0x08c1), .driver_info = USB_QUIRK_RESET_RESUME }, + +@@ -78,6 +85,12 @@ static const struct usb_device_id usb_quirk_list[] = { + /* Philips PSC805 audio device */ + { USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME }, + ++ /* Plantronic Audio 655 DSP */ ++ { USB_DEVICE(0x047f, 0xc008), .driver_info = USB_QUIRK_RESET_RESUME }, ++ ++ /* Plantronic Audio 648 USB */ ++ { USB_DEVICE(0x047f, 0xc013), .driver_info = USB_QUIRK_RESET_RESUME }, ++ + /* Artisman Watchdog Dongle */ + { USB_DEVICE(0x04b4, 0x0526), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, +diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c +index 9a8c936cd42c..41f841fa6c4d 100644 +--- a/drivers/usb/host/xhci-mem.c ++++ b/drivers/usb/host/xhci-mem.c +@@ -1498,10 +1498,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, + * use Event Data TRBs, and we don't chain in a link TRB on short + * transfers, we're basically dividing by 1. + * +- * xHCI 1.0 specification indicates that the Average TRB Length should +- * be set to 8 for control endpoints. ++ * xHCI 1.0 and 1.1 specification indicates that the Average TRB Length ++ * should be set to 8 for control endpoints. + */ +- if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version == 0x100) ++ if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version >= 0x100) + ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8)); + else + ep_ctx->tx_info |= +@@ -1792,8 +1792,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) + int size; + int i, j, num_ports; + +- if (timer_pending(&xhci->cmd_timer)) +- del_timer_sync(&xhci->cmd_timer); ++ del_timer_sync(&xhci->cmd_timer); + + /* Free the Event Ring Segment Table and the actual Event Ring */ + size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); +@@ -2321,6 +2320,10 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) + + INIT_LIST_HEAD(&xhci->cmd_list); + ++ /* init command timeout timer */ ++ setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, ++ (unsigned long)xhci); ++ + page_size = readl(&xhci->op_regs->page_size); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "Supported page size register = 0x%x", page_size); +@@ -2505,10 +2508,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) + "Wrote ERST address to ir_set 0."); + xhci_print_ir_set(xhci, 0); + +- /* init command timeout timer */ +- setup_timer(&xhci->cmd_timer, xhci_handle_command_timeout, +- (unsigned long)xhci); +- + /* + * XXX: Might need to set the Interrupter Moderation Register to + * something other than the default (~1ms minimum between interrupts). +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +index 5590eac2b22d..c79d33676672 100644 +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -180,51 +180,6 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) + "QUIRK: Resetting on resume"); + } + +-/* +- * In some Intel xHCI controllers, in order to get D3 working, +- * through a vendor specific SSIC CONFIG register at offset 0x883c, +- * SSIC PORT need to be marked as "unused" before putting xHCI +- * into D3. After D3 exit, the SSIC port need to be marked as "used". +- * Without this change, xHCI might not enter D3 state. +- * Make sure PME works on some Intel xHCI controllers by writing 1 to clear +- * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 +- */ +-static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend) +-{ +- struct xhci_hcd *xhci = hcd_to_xhci(hcd); +- struct pci_dev *pdev = to_pci_dev(hcd->self.controller); +- u32 val; +- void __iomem *reg; +- +- if (pdev->vendor == PCI_VENDOR_ID_INTEL && +- pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { +- +- reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2; +- +- /* Notify SSIC that SSIC profile programming is not done */ +- val = readl(reg) & ~PROG_DONE; +- writel(val, reg); +- +- /* Mark SSIC port as unused(suspend) or used(resume) */ +- val = readl(reg); +- if (suspend) +- val |= SSIC_PORT_UNUSED; +- else +- val &= ~SSIC_PORT_UNUSED; +- writel(val, reg); +- +- /* Notify SSIC that SSIC profile programming is done */ +- val = readl(reg) | PROG_DONE; +- writel(val, reg); +- readl(reg); +- } +- +- reg = (void __iomem *) xhci->cap_regs + 0x80a4; +- val = readl(reg); +- writel(val | BIT(28), reg); +- readl(reg); +-} +- + #ifdef CONFIG_ACPI + static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) + { +@@ -345,6 +300,51 @@ static void xhci_pci_remove(struct pci_dev *dev) + } + + #ifdef CONFIG_PM ++/* ++ * In some Intel xHCI controllers, in order to get D3 working, ++ * through a vendor specific SSIC CONFIG register at offset 0x883c, ++ * SSIC PORT need to be marked as "unused" before putting xHCI ++ * into D3. After D3 exit, the SSIC port need to be marked as "used". ++ * Without this change, xHCI might not enter D3 state. ++ * Make sure PME works on some Intel xHCI controllers by writing 1 to clear ++ * the Internal PME flag bit in vendor specific PMCTRL register at offset 0x80a4 ++ */ ++static void xhci_pme_quirk(struct usb_hcd *hcd, bool suspend) ++{ ++ struct xhci_hcd *xhci = hcd_to_xhci(hcd); ++ struct pci_dev *pdev = to_pci_dev(hcd->self.controller); ++ u32 val; ++ void __iomem *reg; ++ ++ if (pdev->vendor == PCI_VENDOR_ID_INTEL && ++ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI) { ++ ++ reg = (void __iomem *) xhci->cap_regs + PORT2_SSIC_CONFIG_REG2; ++ ++ /* Notify SSIC that SSIC profile programming is not done */ ++ val = readl(reg) & ~PROG_DONE; ++ writel(val, reg); ++ ++ /* Mark SSIC port as unused(suspend) or used(resume) */ ++ val = readl(reg); ++ if (suspend) ++ val |= SSIC_PORT_UNUSED; ++ else ++ val &= ~SSIC_PORT_UNUSED; ++ writel(val, reg); ++ ++ /* Notify SSIC that SSIC profile programming is done */ ++ val = readl(reg) | PROG_DONE; ++ writel(val, reg); ++ readl(reg); ++ } ++ ++ reg = (void __iomem *) xhci->cap_regs + 0x80a4; ++ val = readl(reg); ++ writel(val | BIT(28), reg); ++ readl(reg); ++} ++ + static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) + { + struct xhci_hcd *xhci = hcd_to_xhci(hcd); +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +index 32f4d564494a..8aadf3def901 100644 +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -302,6 +302,15 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci) + ret = xhci_handshake(&xhci->op_regs->cmd_ring, + CMD_RING_RUNNING, 0, 5 * 1000 * 1000); + if (ret < 0) { ++ /* we are about to kill xhci, give it one more chance */ ++ xhci_write_64(xhci, temp_64 | CMD_RING_ABORT, ++ &xhci->op_regs->cmd_ring); ++ udelay(1000); ++ ret = xhci_handshake(&xhci->op_regs->cmd_ring, ++ CMD_RING_RUNNING, 0, 3 * 1000 * 1000); ++ if (ret == 0) ++ return 0; ++ + xhci_err(xhci, "Stopped the command ring failed, " + "maybe the host is dead\n"); + xhci->xhc_state |= XHCI_STATE_DYING; +@@ -3041,9 +3050,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + struct xhci_td *td; + struct scatterlist *sg; + int num_sgs; +- int trb_buff_len, this_sg_len, running_total; ++ int trb_buff_len, this_sg_len, running_total, ret; + unsigned int total_packet_count; ++ bool zero_length_needed; + bool first_trb; ++ int last_trb_num; + u64 addr; + bool more_trbs_coming; + +@@ -3059,13 +3070,27 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + total_packet_count = DIV_ROUND_UP(urb->transfer_buffer_length, + usb_endpoint_maxp(&urb->ep->desc)); + +- trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ++ ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, + num_trbs, urb, 0, mem_flags); +- if (trb_buff_len < 0) +- return trb_buff_len; ++ if (ret < 0) ++ return ret; + + urb_priv = urb->hcpriv; ++ ++ /* Deal with URB_ZERO_PACKET - need one more td/trb */ ++ zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET && ++ urb_priv->length == 2; ++ if (zero_length_needed) { ++ num_trbs++; ++ xhci_dbg(xhci, "Creating zero length td.\n"); ++ ret = prepare_transfer(xhci, xhci->devs[slot_id], ++ ep_index, urb->stream_id, ++ 1, urb, 1, mem_flags); ++ if (ret < 0) ++ return ret; ++ } ++ + td = urb_priv->td[0]; + + /* +@@ -3095,6 +3120,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + trb_buff_len = urb->transfer_buffer_length; + + first_trb = true; ++ last_trb_num = zero_length_needed ? 2 : 1; + /* Queue the first TRB, even if it's zero-length */ + do { + u32 field = 0; +@@ -3112,12 +3138,15 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + /* Chain all the TRBs together; clear the chain bit in the last + * TRB to indicate it's the last TRB in the chain. + */ +- if (num_trbs > 1) { ++ if (num_trbs > last_trb_num) { + field |= TRB_CHAIN; +- } else { +- /* FIXME - add check for ZERO_PACKET flag before this */ ++ } else if (num_trbs == last_trb_num) { + td->last_trb = ep_ring->enqueue; + field |= TRB_IOC; ++ } else if (zero_length_needed && num_trbs == 1) { ++ trb_buff_len = 0; ++ urb_priv->td[1]->last_trb = ep_ring->enqueue; ++ field |= TRB_IOC; + } + + /* Only set interrupt on short packet for IN endpoints */ +@@ -3179,7 +3208,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + if (running_total + trb_buff_len > urb->transfer_buffer_length) + trb_buff_len = + urb->transfer_buffer_length - running_total; +- } while (running_total < urb->transfer_buffer_length); ++ } while (num_trbs > 0); + + check_trb_math(urb, num_trbs, running_total); + giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, +@@ -3197,7 +3226,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + int num_trbs; + struct xhci_generic_trb *start_trb; + bool first_trb; ++ int last_trb_num; + bool more_trbs_coming; ++ bool zero_length_needed; + int start_cycle; + u32 field, length_field; + +@@ -3228,7 +3259,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + num_trbs++; + running_total += TRB_MAX_BUFF_SIZE; + } +- /* FIXME: this doesn't deal with URB_ZERO_PACKET - need one more */ + + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, +@@ -3237,6 +3267,20 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + return ret; + + urb_priv = urb->hcpriv; ++ ++ /* Deal with URB_ZERO_PACKET - need one more td/trb */ ++ zero_length_needed = urb->transfer_flags & URB_ZERO_PACKET && ++ urb_priv->length == 2; ++ if (zero_length_needed) { ++ num_trbs++; ++ xhci_dbg(xhci, "Creating zero length td.\n"); ++ ret = prepare_transfer(xhci, xhci->devs[slot_id], ++ ep_index, urb->stream_id, ++ 1, urb, 1, mem_flags); ++ if (ret < 0) ++ return ret; ++ } ++ + td = urb_priv->td[0]; + + /* +@@ -3258,7 +3302,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + trb_buff_len = urb->transfer_buffer_length; + + first_trb = true; +- ++ last_trb_num = zero_length_needed ? 2 : 1; + /* Queue the first TRB, even if it's zero-length */ + do { + u32 remainder = 0; +@@ -3275,12 +3319,15 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + /* Chain all the TRBs together; clear the chain bit in the last + * TRB to indicate it's the last TRB in the chain. + */ +- if (num_trbs > 1) { ++ if (num_trbs > last_trb_num) { + field |= TRB_CHAIN; +- } else { +- /* FIXME - add check for ZERO_PACKET flag before this */ ++ } else if (num_trbs == last_trb_num) { + td->last_trb = ep_ring->enqueue; + field |= TRB_IOC; ++ } else if (zero_length_needed && num_trbs == 1) { ++ trb_buff_len = 0; ++ urb_priv->td[1]->last_trb = ep_ring->enqueue; ++ field |= TRB_IOC; + } + + /* Only set interrupt on short packet for IN endpoints */ +@@ -3318,7 +3365,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + trb_buff_len = urb->transfer_buffer_length - running_total; + if (trb_buff_len > TRB_MAX_BUFF_SIZE) + trb_buff_len = TRB_MAX_BUFF_SIZE; +- } while (running_total < urb->transfer_buffer_length); ++ } while (num_trbs > 0); + + check_trb_math(urb, num_trbs, running_total); + giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, +@@ -3385,8 +3432,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, + if (start_cycle == 0) + field |= 0x1; + +- /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ +- if (xhci->hci_version == 0x100) { ++ /* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */ ++ if (xhci->hci_version >= 0x100) { + if (urb->transfer_buffer_length > 0) { + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_TX_TYPE(TRB_DATA_IN); +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +index 526ebc0c7e72..d7b9f484d4e9 100644 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -146,7 +146,8 @@ static int xhci_start(struct xhci_hcd *xhci) + "waited %u microseconds.\n", + XHCI_MAX_HALT_USEC); + if (!ret) +- xhci->xhc_state &= ~XHCI_STATE_HALTED; ++ xhci->xhc_state &= ~(XHCI_STATE_HALTED | XHCI_STATE_DYING); ++ + return ret; + } + +@@ -654,15 +655,6 @@ int xhci_run(struct usb_hcd *hcd) + } + EXPORT_SYMBOL_GPL(xhci_run); + +-static void xhci_only_stop_hcd(struct usb_hcd *hcd) +-{ +- struct xhci_hcd *xhci = hcd_to_xhci(hcd); +- +- spin_lock_irq(&xhci->lock); +- xhci_halt(xhci); +- spin_unlock_irq(&xhci->lock); +-} +- + /* + * Stop xHCI driver. + * +@@ -677,12 +669,14 @@ void xhci_stop(struct usb_hcd *hcd) + u32 temp; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + +- if (!usb_hcd_is_primary_hcd(hcd)) { +- xhci_only_stop_hcd(xhci->shared_hcd); ++ if (xhci->xhc_state & XHCI_STATE_HALTED) + return; +- } + ++ mutex_lock(&xhci->mutex); + spin_lock_irq(&xhci->lock); ++ xhci->xhc_state |= XHCI_STATE_HALTED; ++ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED; ++ + /* Make sure the xHC is halted for a USB3 roothub + * (xhci_stop() could be called as part of failed init). + */ +@@ -717,6 +711,7 @@ void xhci_stop(struct usb_hcd *hcd) + xhci_dbg_trace(xhci, trace_xhci_dbg_init, + "xhci_stop completed - status = %x", + readl(&xhci->op_regs->status)); ++ mutex_unlock(&xhci->mutex); + } + + /* +@@ -1340,6 +1335,11 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) + + if (usb_endpoint_xfer_isoc(&urb->ep->desc)) + size = urb->number_of_packets; ++ else if (usb_endpoint_is_bulk_out(&urb->ep->desc) && ++ urb->transfer_buffer_length > 0 && ++ urb->transfer_flags & URB_ZERO_PACKET && ++ !(urb->transfer_buffer_length % usb_endpoint_maxp(&urb->ep->desc))) ++ size = 2; + else + size = 1; + +@@ -3788,6 +3788,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, + + mutex_lock(&xhci->mutex); + ++ if (xhci->xhc_state) /* dying or halted */ ++ goto out; ++ + if (!udev->slot_id) { + xhci_dbg_trace(xhci, trace_xhci_dbg_address, + "Bad Slot ID %d", udev->slot_id); +diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c +index 3ad5d19e4d04..23c794813e6a 100644 +--- a/drivers/usb/misc/chaoskey.c ++++ b/drivers/usb/misc/chaoskey.c +@@ -472,7 +472,7 @@ static int chaoskey_rng_read(struct hwrng *rng, void *data, + if (this_time > max) + this_time = max; + +- memcpy(data, dev->buf, this_time); ++ memcpy(data, dev->buf + dev->used, this_time); + + dev->used += this_time; + +diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c +index 4d1b44c232ee..d07cafb7d5f5 100644 +--- a/drivers/usb/musb/musb_cppi41.c ++++ b/drivers/usb/musb/musb_cppi41.c +@@ -614,7 +614,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) + { + struct musb *musb = controller->musb; + struct device *dev = musb->controller; +- struct device_node *np = dev->of_node; ++ struct device_node *np = dev->parent->of_node; + struct cppi41_dma_channel *cppi41_channel; + int count; + int i; +@@ -664,7 +664,7 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) + musb_dma->status = MUSB_DMA_STATUS_FREE; + musb_dma->max_len = SZ_4M; + +- dc = dma_request_slave_channel(dev, str); ++ dc = dma_request_slave_channel(dev->parent, str); + if (!dc) { + dev_err(dev, "Failed to request %s.\n", str); + ret = -EPROBE_DEFER; +@@ -695,7 +695,7 @@ cppi41_dma_controller_create(struct musb *musb, void __iomem *base) + struct cppi41_dma_controller *controller; + int ret = 0; + +- if (!musb->controller->of_node) { ++ if (!musb->controller->parent->of_node) { + dev_err(musb->controller, "Need DT for the DMA engine.\n"); + return NULL; + } +diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c +index 1334a3de31b8..67325ec94894 100644 +--- a/drivers/usb/musb/musb_dsps.c ++++ b/drivers/usb/musb/musb_dsps.c +@@ -225,8 +225,11 @@ static void dsps_musb_enable(struct musb *musb) + + dsps_writel(reg_base, wrp->epintr_set, epmask); + dsps_writel(reg_base, wrp->coreintr_set, coremask); +- /* start polling for ID change. */ +- mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout)); ++ /* start polling for ID change in dual-role idle mode */ ++ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE && ++ musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE) ++ mod_timer(&glue->timer, jiffies + ++ msecs_to_jiffies(wrp->poll_timeout)); + dsps_musb_try_idle(musb, 0); + } + +diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c +index deee68eafb72..0cd85f2ccddd 100644 +--- a/drivers/usb/phy/phy-generic.c ++++ b/drivers/usb/phy/phy-generic.c +@@ -230,7 +230,8 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop, + clk_rate = pdata->clk_rate; + needs_vcc = pdata->needs_vcc; + if (gpio_is_valid(pdata->gpio_reset)) { +- err = devm_gpio_request_one(dev, pdata->gpio_reset, 0, ++ err = devm_gpio_request_one(dev, pdata->gpio_reset, ++ GPIOF_ACTIVE_LOW, + dev_name(dev)); + if (!err) + nop->gpiod_reset = +diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c +index 876423b8892c..7c8eb4c4c175 100644 +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -278,6 +278,10 @@ static void option_instat_callback(struct urb *urb); + #define ZTE_PRODUCT_MF622 0x0001 + #define ZTE_PRODUCT_MF628 0x0015 + #define ZTE_PRODUCT_MF626 0x0031 ++#define ZTE_PRODUCT_ZM8620_X 0x0396 ++#define ZTE_PRODUCT_ME3620_MBIM 0x0426 ++#define ZTE_PRODUCT_ME3620_X 0x1432 ++#define ZTE_PRODUCT_ME3620_L 0x1433 + #define ZTE_PRODUCT_AC2726 0xfff1 + #define ZTE_PRODUCT_MG880 0xfffd + #define ZTE_PRODUCT_CDMA_TECH 0xfffe +@@ -544,6 +548,18 @@ static const struct option_blacklist_info zte_mc2716_z_blacklist = { + .sendsetup = BIT(1) | BIT(2) | BIT(3), + }; + ++static const struct option_blacklist_info zte_me3620_mbim_blacklist = { ++ .reserved = BIT(2) | BIT(3) | BIT(4), ++}; ++ ++static const struct option_blacklist_info zte_me3620_xl_blacklist = { ++ .reserved = BIT(3) | BIT(4) | BIT(5), ++}; ++ ++static const struct option_blacklist_info zte_zm8620_x_blacklist = { ++ .reserved = BIT(3) | BIT(4) | BIT(5), ++}; ++ + static const struct option_blacklist_info huawei_cdc12_blacklist = { + .reserved = BIT(1) | BIT(2), + }; +@@ -1591,6 +1607,14 @@ static const struct usb_device_id option_ids[] = { + .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist }, ++ { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_L), ++ .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist }, ++ { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_MBIM), ++ .driver_info = (kernel_ulong_t)&zte_me3620_mbim_blacklist }, ++ { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ME3620_X), ++ .driver_info = (kernel_ulong_t)&zte_me3620_xl_blacklist }, ++ { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_ZM8620_X), ++ .driver_info = (kernel_ulong_t)&zte_zm8620_x_blacklist }, + { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, + { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, +diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c +index 6c3734d2b45a..d3ea90bef84d 100644 +--- a/drivers/usb/serial/whiteheat.c ++++ b/drivers/usb/serial/whiteheat.c +@@ -80,6 +80,8 @@ static int whiteheat_firmware_download(struct usb_serial *serial, + static int whiteheat_firmware_attach(struct usb_serial *serial); + + /* function prototypes for the Connect Tech WhiteHEAT serial converter */ ++static int whiteheat_probe(struct usb_serial *serial, ++ const struct usb_device_id *id); + static int whiteheat_attach(struct usb_serial *serial); + static void whiteheat_release(struct usb_serial *serial); + static int whiteheat_port_probe(struct usb_serial_port *port); +@@ -116,6 +118,7 @@ static struct usb_serial_driver whiteheat_device = { + .description = "Connect Tech - WhiteHEAT", + .id_table = id_table_std, + .num_ports = 4, ++ .probe = whiteheat_probe, + .attach = whiteheat_attach, + .release = whiteheat_release, + .port_probe = whiteheat_port_probe, +@@ -217,6 +220,34 @@ static int whiteheat_firmware_attach(struct usb_serial *serial) + /***************************************************************************** + * Connect Tech's White Heat serial driver functions + *****************************************************************************/ ++ ++static int whiteheat_probe(struct usb_serial *serial, ++ const struct usb_device_id *id) ++{ ++ struct usb_host_interface *iface_desc; ++ struct usb_endpoint_descriptor *endpoint; ++ size_t num_bulk_in = 0; ++ size_t num_bulk_out = 0; ++ size_t min_num_bulk; ++ unsigned int i; ++ ++ iface_desc = serial->interface->cur_altsetting; ++ ++ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { ++ endpoint = &iface_desc->endpoint[i].desc; ++ if (usb_endpoint_is_bulk_in(endpoint)) ++ ++num_bulk_in; ++ if (usb_endpoint_is_bulk_out(endpoint)) ++ ++num_bulk_out; ++ } ++ ++ min_num_bulk = COMMAND_PORT + 1; ++ if (num_bulk_in < min_num_bulk || num_bulk_out < min_num_bulk) ++ return -ENODEV; ++ ++ return 0; ++} ++ + static int whiteheat_attach(struct usb_serial *serial) + { + struct usb_serial_port *command_port; +diff --git a/drivers/watchdog/imgpdc_wdt.c b/drivers/watchdog/imgpdc_wdt.c +index 0f73621827ab..15ab07230960 100644 +--- a/drivers/watchdog/imgpdc_wdt.c ++++ b/drivers/watchdog/imgpdc_wdt.c +@@ -316,6 +316,7 @@ static int pdc_wdt_remove(struct platform_device *pdev) + { + struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev); + ++ unregister_restart_handler(&pdc_wdt->restart_handler); + pdc_wdt_stop(&pdc_wdt->wdt_dev); + watchdog_unregister_device(&pdc_wdt->wdt_dev); + clk_disable_unprepare(pdc_wdt->wdt_clk); +diff --git a/drivers/watchdog/sunxi_wdt.c b/drivers/watchdog/sunxi_wdt.c +index a29afb37c48c..47bd8a14d01f 100644 +--- a/drivers/watchdog/sunxi_wdt.c ++++ b/drivers/watchdog/sunxi_wdt.c +@@ -184,7 +184,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev) + /* Set system reset function */ + reg = readl(wdt_base + regs->wdt_cfg); + reg &= ~(regs->wdt_reset_mask); +- reg |= ~(regs->wdt_reset_val); ++ reg |= regs->wdt_reset_val; + writel(reg, wdt_base + regs->wdt_cfg); + + /* Enable watchdog */ +diff --git a/drivers/xen/preempt.c b/drivers/xen/preempt.c +index a1800c150839..08cb419eb4e6 100644 +--- a/drivers/xen/preempt.c ++++ b/drivers/xen/preempt.c +@@ -31,7 +31,7 @@ EXPORT_SYMBOL_GPL(xen_in_preemptible_hcall); + asmlinkage __visible void xen_maybe_preempt_hcall(void) + { + if (unlikely(__this_cpu_read(xen_in_preemptible_hcall) +- && should_resched())) { ++ && need_resched())) { + /* + * Clear flag as we may be rescheduled on a different + * cpu. +diff --git a/fs/block_dev.c b/fs/block_dev.c +index 198243717da5..1170f8ce5e7f 100644 +--- a/fs/block_dev.c ++++ b/fs/block_dev.c +@@ -1241,6 +1241,13 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) + goto out_clear; + } + bd_set_size(bdev, (loff_t)bdev->bd_part->nr_sects << 9); ++ /* ++ * If the partition is not aligned on a page ++ * boundary, we can't do dax I/O to it. ++ */ ++ if ((bdev->bd_part->start_sect % (PAGE_SIZE / 512)) || ++ (bdev->bd_part->nr_sects % (PAGE_SIZE / 512))) ++ bdev->bd_inode->i_flags &= ~S_DAX; + } + } else { + if (bdev->bd_contains == bdev) { +diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c +index 02d05817cbdf..3fc4fec9b94e 100644 +--- a/fs/btrfs/extent_io.c ++++ b/fs/btrfs/extent_io.c +@@ -2798,7 +2798,8 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree, + bio_end_io_t end_io_func, + int mirror_num, + unsigned long prev_bio_flags, +- unsigned long bio_flags) ++ unsigned long bio_flags, ++ bool force_bio_submit) + { + int ret = 0; + struct bio *bio; +@@ -2816,6 +2817,7 @@ static int submit_extent_page(int rw, struct extent_io_tree *tree, + contig = bio_end_sector(bio) == sector; + + if (prev_bio_flags != bio_flags || !contig || ++ force_bio_submit || + merge_bio(rw, tree, page, offset, page_size, bio, bio_flags) || + bio_add_page(bio, page, page_size, offset) < page_size) { + ret = submit_one_bio(rw, bio, mirror_num, +@@ -2909,7 +2911,8 @@ static int __do_readpage(struct extent_io_tree *tree, + get_extent_t *get_extent, + struct extent_map **em_cached, + struct bio **bio, int mirror_num, +- unsigned long *bio_flags, int rw) ++ unsigned long *bio_flags, int rw, ++ u64 *prev_em_start) + { + struct inode *inode = page->mapping->host; + u64 start = page_offset(page); +@@ -2957,6 +2960,7 @@ static int __do_readpage(struct extent_io_tree *tree, + } + while (cur <= end) { + unsigned long pnr = (last_byte >> PAGE_CACHE_SHIFT) + 1; ++ bool force_bio_submit = false; + + if (cur >= last_byte) { + char *userpage; +@@ -3007,6 +3011,49 @@ static int __do_readpage(struct extent_io_tree *tree, + block_start = em->block_start; + if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) + block_start = EXTENT_MAP_HOLE; ++ ++ /* ++ * If we have a file range that points to a compressed extent ++ * and it's followed by a consecutive file range that points to ++ * to the same compressed extent (possibly with a different ++ * offset and/or length, so it either points to the whole extent ++ * or only part of it), we must make sure we do not submit a ++ * single bio to populate the pages for the 2 ranges because ++ * this makes the compressed extent read zero out the pages ++ * belonging to the 2nd range. Imagine the following scenario: ++ * ++ * File layout ++ * [0 - 8K] [8K - 24K] ++ * | | ++ * | | ++ * points to extent X, points to extent X, ++ * offset 4K, length of 8K offset 0, length 16K ++ * ++ * [extent X, compressed length = 4K uncompressed length = 16K] ++ * ++ * If the bio to read the compressed extent covers both ranges, ++ * it will decompress extent X into the pages belonging to the ++ * first range and then it will stop, zeroing out the remaining ++ * pages that belong to the other range that points to extent X. ++ * So here we make sure we submit 2 bios, one for the first ++ * range and another one for the third range. Both will target ++ * the same physical extent from disk, but we can't currently ++ * make the compressed bio endio callback populate the pages ++ * for both ranges because each compressed bio is tightly ++ * coupled with a single extent map, and each range can have ++ * an extent map with a different offset value relative to the ++ * uncompressed data of our extent and different lengths. This ++ * is a corner case so we prioritize correctness over ++ * non-optimal behavior (submitting 2 bios for the same extent). ++ */ ++ if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags) && ++ prev_em_start && *prev_em_start != (u64)-1 && ++ *prev_em_start != em->orig_start) ++ force_bio_submit = true; ++ ++ if (prev_em_start) ++ *prev_em_start = em->orig_start; ++ + free_extent_map(em); + em = NULL; + +@@ -3056,7 +3103,8 @@ static int __do_readpage(struct extent_io_tree *tree, + bdev, bio, pnr, + end_bio_extent_readpage, mirror_num, + *bio_flags, +- this_bio_flag); ++ this_bio_flag, ++ force_bio_submit); + if (!ret) { + nr++; + *bio_flags = this_bio_flag; +@@ -3083,7 +3131,8 @@ static inline void __do_contiguous_readpages(struct extent_io_tree *tree, + get_extent_t *get_extent, + struct extent_map **em_cached, + struct bio **bio, int mirror_num, +- unsigned long *bio_flags, int rw) ++ unsigned long *bio_flags, int rw, ++ u64 *prev_em_start) + { + struct inode *inode; + struct btrfs_ordered_extent *ordered; +@@ -3103,7 +3152,7 @@ static inline void __do_contiguous_readpages(struct extent_io_tree *tree, + + for (index = 0; index < nr_pages; index++) { + __do_readpage(tree, pages[index], get_extent, em_cached, bio, +- mirror_num, bio_flags, rw); ++ mirror_num, bio_flags, rw, prev_em_start); + page_cache_release(pages[index]); + } + } +@@ -3113,7 +3162,8 @@ static void __extent_readpages(struct extent_io_tree *tree, + int nr_pages, get_extent_t *get_extent, + struct extent_map **em_cached, + struct bio **bio, int mirror_num, +- unsigned long *bio_flags, int rw) ++ unsigned long *bio_flags, int rw, ++ u64 *prev_em_start) + { + u64 start = 0; + u64 end = 0; +@@ -3134,7 +3184,7 @@ static void __extent_readpages(struct extent_io_tree *tree, + index - first_index, start, + end, get_extent, em_cached, + bio, mirror_num, bio_flags, +- rw); ++ rw, prev_em_start); + start = page_start; + end = start + PAGE_CACHE_SIZE - 1; + first_index = index; +@@ -3145,7 +3195,8 @@ static void __extent_readpages(struct extent_io_tree *tree, + __do_contiguous_readpages(tree, &pages[first_index], + index - first_index, start, + end, get_extent, em_cached, bio, +- mirror_num, bio_flags, rw); ++ mirror_num, bio_flags, rw, ++ prev_em_start); + } + + static int __extent_read_full_page(struct extent_io_tree *tree, +@@ -3171,7 +3222,7 @@ static int __extent_read_full_page(struct extent_io_tree *tree, + } + + ret = __do_readpage(tree, page, get_extent, NULL, bio, mirror_num, +- bio_flags, rw); ++ bio_flags, rw, NULL); + return ret; + } + +@@ -3197,7 +3248,7 @@ int extent_read_full_page_nolock(struct extent_io_tree *tree, struct page *page, + int ret; + + ret = __do_readpage(tree, page, get_extent, NULL, &bio, mirror_num, +- &bio_flags, READ); ++ &bio_flags, READ, NULL); + if (bio) + ret = submit_one_bio(READ, bio, mirror_num, bio_flags); + return ret; +@@ -3450,7 +3501,7 @@ static noinline_for_stack int __extent_writepage_io(struct inode *inode, + sector, iosize, pg_offset, + bdev, &epd->bio, max_nr, + end_bio_extent_writepage, +- 0, 0, 0); ++ 0, 0, 0, false); + if (ret) + SetPageError(page); + } +@@ -3752,7 +3803,7 @@ static noinline_for_stack int write_one_eb(struct extent_buffer *eb, + ret = submit_extent_page(rw, tree, p, offset >> 9, + PAGE_CACHE_SIZE, 0, bdev, &epd->bio, + -1, end_bio_extent_buffer_writepage, +- 0, epd->bio_flags, bio_flags); ++ 0, epd->bio_flags, bio_flags, false); + epd->bio_flags = bio_flags; + if (ret) { + set_btree_ioerr(p); +@@ -4156,6 +4207,7 @@ int extent_readpages(struct extent_io_tree *tree, + struct page *page; + struct extent_map *em_cached = NULL; + int nr = 0; ++ u64 prev_em_start = (u64)-1; + + for (page_idx = 0; page_idx < nr_pages; page_idx++) { + page = list_entry(pages->prev, struct page, lru); +@@ -4172,12 +4224,12 @@ int extent_readpages(struct extent_io_tree *tree, + if (nr < ARRAY_SIZE(pagepool)) + continue; + __extent_readpages(tree, pagepool, nr, get_extent, &em_cached, +- &bio, 0, &bio_flags, READ); ++ &bio, 0, &bio_flags, READ, &prev_em_start); + nr = 0; + } + if (nr) + __extent_readpages(tree, pagepool, nr, get_extent, &em_cached, +- &bio, 0, &bio_flags, READ); ++ &bio, 0, &bio_flags, READ, &prev_em_start); + + if (em_cached) + free_extent_map(em_cached); +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index e33dff356460..b54e63038b96 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -5051,7 +5051,8 @@ void btrfs_evict_inode(struct inode *inode) + goto no_delete; + } + /* do we really want it for ->i_nlink > 0 and zero btrfs_root_refs? */ +- btrfs_wait_ordered_range(inode, 0, (u64)-1); ++ if (!special_file(inode->i_mode)) ++ btrfs_wait_ordered_range(inode, 0, (u64)-1); + + btrfs_free_io_failure_record(inode, 0, (u64)-1); + +diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c +index aa0dc2573374..afa09fce8151 100644 +--- a/fs/cifs/cifsencrypt.c ++++ b/fs/cifs/cifsencrypt.c +@@ -444,6 +444,48 @@ find_domain_name(struct cifs_ses *ses, const struct nls_table *nls_cp) + return 0; + } + ++/* Server has provided av pairs/target info in the type 2 challenge ++ * packet and we have plucked it and stored within smb session. ++ * We parse that blob here to find the server given timestamp ++ * as part of ntlmv2 authentication (or local current time as ++ * default in case of failure) ++ */ ++static __le64 ++find_timestamp(struct cifs_ses *ses) ++{ ++ unsigned int attrsize; ++ unsigned int type; ++ unsigned int onesize = sizeof(struct ntlmssp2_name); ++ unsigned char *blobptr; ++ unsigned char *blobend; ++ struct ntlmssp2_name *attrptr; ++ ++ if (!ses->auth_key.len || !ses->auth_key.response) ++ return 0; ++ ++ blobptr = ses->auth_key.response; ++ blobend = blobptr + ses->auth_key.len; ++ ++ while (blobptr + onesize < blobend) { ++ attrptr = (struct ntlmssp2_name *) blobptr; ++ type = le16_to_cpu(attrptr->type); ++ if (type == NTLMSSP_AV_EOL) ++ break; ++ blobptr += 2; /* advance attr type */ ++ attrsize = le16_to_cpu(attrptr->length); ++ blobptr += 2; /* advance attr size */ ++ if (blobptr + attrsize > blobend) ++ break; ++ if (type == NTLMSSP_AV_TIMESTAMP) { ++ if (attrsize == sizeof(u64)) ++ return *((__le64 *)blobptr); ++ } ++ blobptr += attrsize; /* advance attr value */ ++ } ++ ++ return cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); ++} ++ + static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, + const struct nls_table *nls_cp) + { +@@ -641,6 +683,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) + struct ntlmv2_resp *ntlmv2; + char ntlmv2_hash[16]; + unsigned char *tiblob = NULL; /* target info blob */ ++ __le64 rsp_timestamp; + + if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) { + if (!ses->domainName) { +@@ -659,6 +702,12 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) + } + } + ++ /* Must be within 5 minutes of the server (or in range +/-2h ++ * in case of Mac OS X), so simply carry over server timestamp ++ * (as Windows 7 does) ++ */ ++ rsp_timestamp = find_timestamp(ses); ++ + baselen = CIFS_SESS_KEY_SIZE + sizeof(struct ntlmv2_resp); + tilen = ses->auth_key.len; + tiblob = ses->auth_key.response; +@@ -675,8 +724,8 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) + (ses->auth_key.response + CIFS_SESS_KEY_SIZE); + ntlmv2->blob_signature = cpu_to_le32(0x00000101); + ntlmv2->reserved = 0; +- /* Must be within 5 minutes of the server */ +- ntlmv2->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); ++ ntlmv2->time = rsp_timestamp; ++ + get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); + ntlmv2->reserved2 = 0; + +diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c +index f621b44cb800..6b66dd5d1540 100644 +--- a/fs/cifs/inode.c ++++ b/fs/cifs/inode.c +@@ -2034,7 +2034,6 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, + struct tcon_link *tlink = NULL; + struct cifs_tcon *tcon = NULL; + struct TCP_Server_Info *server; +- struct cifs_io_parms io_parms; + + /* + * To avoid spurious oplock breaks from server, in the case of +@@ -2056,18 +2055,6 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, + rc = -ENOSYS; + cifsFileInfo_put(open_file); + cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); +- if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { +- unsigned int bytes_written; +- +- io_parms.netfid = open_file->fid.netfid; +- io_parms.pid = open_file->pid; +- io_parms.tcon = tcon; +- io_parms.offset = 0; +- io_parms.length = attrs->ia_size; +- rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, +- NULL, NULL, 1); +- cifs_dbg(FYI, "Wrt seteof rc %d\n", rc); +- } + } else + rc = -EINVAL; + +@@ -2093,28 +2080,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, + else + rc = -ENOSYS; + cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); +- if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { +- __u16 netfid; +- int oplock = 0; + +- rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN, +- GENERIC_WRITE, CREATE_NOT_DIR, &netfid, +- &oplock, NULL, cifs_sb->local_nls, +- cifs_remap(cifs_sb)); +- if (rc == 0) { +- unsigned int bytes_written; +- +- io_parms.netfid = netfid; +- io_parms.pid = current->tgid; +- io_parms.tcon = tcon; +- io_parms.offset = 0; +- io_parms.length = attrs->ia_size; +- rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, NULL, +- NULL, 1); +- cifs_dbg(FYI, "wrt seteof rc %d\n", rc); +- CIFSSMBClose(xid, tcon, netfid); +- } +- } + if (tlink) + cifs_put_tlink(tlink); + +diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c +index df91bcf56d67..18da19f4f811 100644 +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -50,9 +50,13 @@ change_conf(struct TCP_Server_Info *server) + break; + default: + server->echoes = true; +- server->oplocks = true; ++ if (enable_oplocks) { ++ server->oplocks = true; ++ server->oplock_credits = 1; ++ } else ++ server->oplocks = false; ++ + server->echo_credits = 1; +- server->oplock_credits = 1; + } + server->credits -= server->echo_credits + server->oplock_credits; + return 0; +diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c +index b8b4f08ee094..60dd83164ed6 100644 +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -46,6 +46,7 @@ + #include "smb2status.h" + #include "smb2glob.h" + #include "cifspdu.h" ++#include "cifs_spnego.h" + + /* + * The following table defines the expected "StructureSize" of SMB2 requests +@@ -486,19 +487,15 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) + cifs_dbg(FYI, "missing security blob on negprot\n"); + + rc = cifs_enable_signing(server, ses->sign); +-#ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */ + if (rc) + goto neg_exit; +- if (blob_length) ++ if (blob_length) { + rc = decode_negTokenInit(security_blob, blob_length, server); +- if (rc == 1) +- rc = 0; +- else if (rc == 0) { +- rc = -EIO; +- goto neg_exit; ++ if (rc == 1) ++ rc = 0; ++ else if (rc == 0) ++ rc = -EIO; + } +-#endif +- + neg_exit: + free_rsp_buf(resp_buftype, rsp); + return rc; +@@ -592,7 +589,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ + struct TCP_Server_Info *server = ses->server; + u16 blob_length = 0; +- char *security_blob; ++ struct key *spnego_key = NULL; ++ char *security_blob = NULL; + char *ntlmssp_blob = NULL; + bool use_spnego = false; /* else use raw ntlmssp */ + +@@ -620,7 +618,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, + ses->ntlmssp->sesskey_per_smbsess = true; + + /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */ +- ses->sectype = RawNTLMSSP; ++ if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP) ++ ses->sectype = RawNTLMSSP; + + ssetup_ntlmssp_authenticate: + if (phase == NtLmChallenge) +@@ -649,7 +648,48 @@ ssetup_ntlmssp_authenticate: + iov[0].iov_base = (char *)req; + /* 4 for rfc1002 length field and 1 for pad */ + iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; +- if (phase == NtLmNegotiate) { ++ ++ if (ses->sectype == Kerberos) { ++#ifdef CONFIG_CIFS_UPCALL ++ struct cifs_spnego_msg *msg; ++ ++ spnego_key = cifs_get_spnego_key(ses); ++ if (IS_ERR(spnego_key)) { ++ rc = PTR_ERR(spnego_key); ++ spnego_key = NULL; ++ goto ssetup_exit; ++ } ++ ++ msg = spnego_key->payload.data; ++ /* ++ * check version field to make sure that cifs.upcall is ++ * sending us a response in an expected form ++ */ ++ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) { ++ cifs_dbg(VFS, ++ "bad cifs.upcall version. Expected %d got %d", ++ CIFS_SPNEGO_UPCALL_VERSION, msg->version); ++ rc = -EKEYREJECTED; ++ goto ssetup_exit; ++ } ++ ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, ++ GFP_KERNEL); ++ if (!ses->auth_key.response) { ++ cifs_dbg(VFS, ++ "Kerberos can't allocate (%u bytes) memory", ++ msg->sesskey_len); ++ rc = -ENOMEM; ++ goto ssetup_exit; ++ } ++ ses->auth_key.len = msg->sesskey_len; ++ blob_length = msg->secblob_len; ++ iov[1].iov_base = msg->data + msg->sesskey_len; ++ iov[1].iov_len = blob_length; ++#else ++ rc = -EOPNOTSUPP; ++ goto ssetup_exit; ++#endif /* CONFIG_CIFS_UPCALL */ ++ } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */ + ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE), + GFP_KERNEL); + if (ntlmssp_blob == NULL) { +@@ -672,6 +712,8 @@ ssetup_ntlmssp_authenticate: + /* with raw NTLMSSP we don't encapsulate in SPNEGO */ + security_blob = ntlmssp_blob; + } ++ iov[1].iov_base = security_blob; ++ iov[1].iov_len = blob_length; + } else if (phase == NtLmAuthenticate) { + req->hdr.SessionId = ses->Suid; + ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500, +@@ -699,6 +741,8 @@ ssetup_ntlmssp_authenticate: + } else { + security_blob = ntlmssp_blob; + } ++ iov[1].iov_base = security_blob; ++ iov[1].iov_len = blob_length; + } else { + cifs_dbg(VFS, "illegal ntlmssp phase\n"); + rc = -EIO; +@@ -710,8 +754,6 @@ ssetup_ntlmssp_authenticate: + cpu_to_le16(sizeof(struct smb2_sess_setup_req) - + 1 /* pad */ - 4 /* rfc1001 len */); + req->SecurityBufferLength = cpu_to_le16(blob_length); +- iov[1].iov_base = security_blob; +- iov[1].iov_len = blob_length; + + inc_rfc1001_len(req, blob_length - 1 /* pad */); + +@@ -722,6 +764,7 @@ ssetup_ntlmssp_authenticate: + + kfree(security_blob); + rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base; ++ ses->Suid = rsp->hdr.SessionId; + if (resp_buftype != CIFS_NO_BUFFER && + rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) { + if (phase != NtLmNegotiate) { +@@ -739,7 +782,6 @@ ssetup_ntlmssp_authenticate: + /* NTLMSSP Negotiate sent now processing challenge (response) */ + phase = NtLmChallenge; /* process ntlmssp challenge */ + rc = 0; /* MORE_PROCESSING is not an error here but expected */ +- ses->Suid = rsp->hdr.SessionId; + rc = decode_ntlmssp_challenge(rsp->Buffer, + le16_to_cpu(rsp->SecurityBufferLength), ses); + } +@@ -796,6 +838,10 @@ keygen_exit: + kfree(ses->auth_key.response); + ses->auth_key.response = NULL; + } ++ if (spnego_key) { ++ key_invalidate(spnego_key); ++ key_put(spnego_key); ++ } + kfree(ses->ntlmssp); + + return rc; +diff --git a/fs/dax.c b/fs/dax.c +index a7f77e1fa18c..ef35a2014580 100644 +--- a/fs/dax.c ++++ b/fs/dax.c +@@ -116,7 +116,8 @@ static ssize_t dax_io(struct inode *inode, struct iov_iter *iter, + unsigned len; + if (pos == max) { + unsigned blkbits = inode->i_blkbits; +- sector_t block = pos >> blkbits; ++ long page = pos >> PAGE_SHIFT; ++ sector_t block = page << (PAGE_SHIFT - blkbits); + unsigned first = pos - (block << blkbits); + long size; + +diff --git a/fs/dcache.c b/fs/dcache.c +index 9b5fe503f6cb..e3b44ca75a1b 100644 +--- a/fs/dcache.c ++++ b/fs/dcache.c +@@ -2926,6 +2926,13 @@ restart: + + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { + struct mount *parent = ACCESS_ONCE(mnt->mnt_parent); ++ /* Escaped? */ ++ if (dentry != vfsmnt->mnt_root) { ++ bptr = *buffer; ++ blen = *buflen; ++ error = 3; ++ break; ++ } + /* Global root? */ + if (mnt != parent) { + dentry = ACCESS_ONCE(mnt->mnt_mountpoint); +diff --git a/fs/namei.c b/fs/namei.c +index 1c2105ed20c5..36df4818a635 100644 +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -560,6 +560,24 @@ static int __nd_alloc_stack(struct nameidata *nd) + return 0; + } + ++/** ++ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root ++ * @path: nameidate to verify ++ * ++ * Rename can sometimes move a file or directory outside of a bind ++ * mount, path_connected allows those cases to be detected. ++ */ ++static bool path_connected(const struct path *path) ++{ ++ struct vfsmount *mnt = path->mnt; ++ ++ /* Only bind mounts can have disconnected paths */ ++ if (mnt->mnt_root == mnt->mnt_sb->s_root) ++ return true; ++ ++ return is_subdir(path->dentry, mnt->mnt_root); ++} ++ + static inline int nd_alloc_stack(struct nameidata *nd) + { + if (likely(nd->depth != EMBEDDED_LEVELS)) +@@ -1296,6 +1314,8 @@ static int follow_dotdot_rcu(struct nameidata *nd) + return -ECHILD; + nd->path.dentry = parent; + nd->seq = seq; ++ if (unlikely(!path_connected(&nd->path))) ++ return -ENOENT; + break; + } else { + struct mount *mnt = real_mount(nd->path.mnt); +@@ -1396,7 +1416,7 @@ static void follow_mount(struct path *path) + } + } + +-static void follow_dotdot(struct nameidata *nd) ++static int follow_dotdot(struct nameidata *nd) + { + if (!nd->root.mnt) + set_root(nd); +@@ -1412,6 +1432,8 @@ static void follow_dotdot(struct nameidata *nd) + /* rare case of legitimate dget_parent()... */ + nd->path.dentry = dget_parent(nd->path.dentry); + dput(old); ++ if (unlikely(!path_connected(&nd->path))) ++ return -ENOENT; + break; + } + if (!follow_up(&nd->path)) +@@ -1419,6 +1441,7 @@ static void follow_dotdot(struct nameidata *nd) + } + follow_mount(&nd->path); + nd->inode = nd->path.dentry->d_inode; ++ return 0; + } + + /* +@@ -1535,8 +1558,6 @@ static int lookup_fast(struct nameidata *nd, + negative = d_is_negative(dentry); + if (read_seqcount_retry(&dentry->d_seq, seq)) + return -ECHILD; +- if (negative) +- return -ENOENT; + + /* + * This sequence count validates that the parent had no +@@ -1557,6 +1578,12 @@ static int lookup_fast(struct nameidata *nd, + goto unlazy; + } + } ++ /* ++ * Note: do negative dentry check after revalidation in ++ * case that drops it. ++ */ ++ if (negative) ++ return -ENOENT; + path->mnt = mnt; + path->dentry = dentry; + if (likely(__follow_mount_rcu(nd, path, inode, seqp))) +@@ -1634,7 +1661,7 @@ static inline int handle_dots(struct nameidata *nd, int type) + if (nd->flags & LOOKUP_RCU) { + return follow_dotdot_rcu(nd); + } else +- follow_dotdot(nd); ++ return follow_dotdot(nd); + } + return 0; + } +diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c +index 029d688a969f..c56886829708 100644 +--- a/fs/nfs/delegation.c ++++ b/fs/nfs/delegation.c +@@ -113,7 +113,8 @@ out: + return status; + } + +-static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) ++static int nfs_delegation_claim_opens(struct inode *inode, ++ const nfs4_stateid *stateid, fmode_t type) + { + struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_open_context *ctx; +@@ -140,7 +141,7 @@ again: + /* Block nfs4_proc_unlck */ + mutex_lock(&sp->so_delegreturn_mutex); + seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); +- err = nfs4_open_delegation_recall(ctx, state, stateid); ++ err = nfs4_open_delegation_recall(ctx, state, stateid, type); + if (!err) + err = nfs_delegation_claim_locks(ctx, state, stateid); + if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) +@@ -411,7 +412,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation + do { + if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) + break; +- err = nfs_delegation_claim_opens(inode, &delegation->stateid); ++ err = nfs_delegation_claim_opens(inode, &delegation->stateid, ++ delegation->type); + if (!issync || err != -EAGAIN) + break; + /* +diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h +index e3c20a3ccc93..785c8525b576 100644 +--- a/fs/nfs/delegation.h ++++ b/fs/nfs/delegation.h +@@ -54,7 +54,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp); + + /* NFSv4 delegation-related procedures */ + int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync); +-int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid); ++int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type); + int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid); + bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags); + +diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c +index b34f2e228601..02ec07973bc4 100644 +--- a/fs/nfs/filelayout/filelayout.c ++++ b/fs/nfs/filelayout/filelayout.c +@@ -629,23 +629,18 @@ out_put: + goto out; + } + +-static void filelayout_free_fh_array(struct nfs4_filelayout_segment *fl) ++static void _filelayout_free_lseg(struct nfs4_filelayout_segment *fl) + { + int i; + +- for (i = 0; i < fl->num_fh; i++) { +- if (!fl->fh_array[i]) +- break; +- kfree(fl->fh_array[i]); ++ if (fl->fh_array) { ++ for (i = 0; i < fl->num_fh; i++) { ++ if (!fl->fh_array[i]) ++ break; ++ kfree(fl->fh_array[i]); ++ } ++ kfree(fl->fh_array); + } +- kfree(fl->fh_array); +- fl->fh_array = NULL; +-} +- +-static void +-_filelayout_free_lseg(struct nfs4_filelayout_segment *fl) +-{ +- filelayout_free_fh_array(fl); + kfree(fl); + } + +@@ -716,21 +711,21 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo, + /* Do we want to use a mempool here? */ + fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), gfp_flags); + if (!fl->fh_array[i]) +- goto out_err_free; ++ goto out_err; + + p = xdr_inline_decode(&stream, 4); + if (unlikely(!p)) +- goto out_err_free; ++ goto out_err; + fl->fh_array[i]->size = be32_to_cpup(p++); + if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) { + printk(KERN_ERR "NFS: Too big fh %d received %d\n", + i, fl->fh_array[i]->size); +- goto out_err_free; ++ goto out_err; + } + + p = xdr_inline_decode(&stream, fl->fh_array[i]->size); + if (unlikely(!p)) +- goto out_err_free; ++ goto out_err; + memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size); + dprintk("DEBUG: %s: fh len %d\n", __func__, + fl->fh_array[i]->size); +@@ -739,8 +734,6 @@ filelayout_decode_layout(struct pnfs_layout_hdr *flo, + __free_page(scratch); + return 0; + +-out_err_free: +- filelayout_free_fh_array(fl); + out_err: + __free_page(scratch); + return -EIO; +diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c +index d731bbf974aa..0f020e4d8421 100644 +--- a/fs/nfs/nfs42proc.c ++++ b/fs/nfs/nfs42proc.c +@@ -175,10 +175,12 @@ loff_t nfs42_proc_llseek(struct file *filep, loff_t offset, int whence) + { + struct nfs_server *server = NFS_SERVER(file_inode(filep)); + struct nfs4_exception exception = { }; +- int err; ++ loff_t err; + + do { + err = _nfs42_proc_llseek(filep, offset, whence); ++ if (err >= 0) ++ break; + if (err == -ENOTSUPP) + return -EOPNOTSUPP; + err = nfs4_handle_exception(server, err, &exception); +diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c +index 73c8204ad463..d2daacad3568 100644 +--- a/fs/nfs/nfs4proc.c ++++ b/fs/nfs/nfs4proc.c +@@ -1127,6 +1127,21 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task) + return ret; + } + ++static bool nfs4_mode_match_open_stateid(struct nfs4_state *state, ++ fmode_t fmode) ++{ ++ switch(fmode & (FMODE_READ|FMODE_WRITE)) { ++ case FMODE_READ|FMODE_WRITE: ++ return state->n_rdwr != 0; ++ case FMODE_WRITE: ++ return state->n_wronly != 0; ++ case FMODE_READ: ++ return state->n_rdonly != 0; ++ } ++ WARN_ON_ONCE(1); ++ return false; ++} ++ + static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode) + { + int ret = 0; +@@ -1561,17 +1576,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context + return opendata; + } + +-static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res) ++static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, ++ fmode_t fmode) + { + struct nfs4_state *newstate; + int ret; + +- if ((opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEGATE_CUR || +- opendata->o_arg.claim == NFS4_OPEN_CLAIM_DELEG_CUR_FH) && +- (opendata->o_arg.u.delegation_type & fmode) != fmode) +- /* This mode can't have been delegated, so we must have +- * a valid open_stateid to cover it - not need to reclaim. +- */ ++ if (!nfs4_mode_match_open_stateid(opendata->state, fmode)) + return 0; + opendata->o_arg.open_flags = 0; + opendata->o_arg.fmode = fmode; +@@ -1587,14 +1598,14 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmod + newstate = nfs4_opendata_to_nfs4_state(opendata); + if (IS_ERR(newstate)) + return PTR_ERR(newstate); ++ if (newstate != opendata->state) ++ ret = -ESTALE; + nfs4_close_state(newstate, fmode); +- *res = newstate; +- return 0; ++ return ret; + } + + static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state) + { +- struct nfs4_state *newstate; + int ret; + + /* Don't trigger recovery in nfs_test_and_clear_all_open_stateid */ +@@ -1605,27 +1616,15 @@ static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state * + clear_bit(NFS_DELEGATED_STATE, &state->flags); + clear_bit(NFS_OPEN_STATE, &state->flags); + smp_rmb(); +- if (state->n_rdwr != 0) { +- ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &newstate); +- if (ret != 0) +- return ret; +- if (newstate != state) +- return -ESTALE; +- } +- if (state->n_wronly != 0) { +- ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &newstate); +- if (ret != 0) +- return ret; +- if (newstate != state) +- return -ESTALE; +- } +- if (state->n_rdonly != 0) { +- ret = nfs4_open_recover_helper(opendata, FMODE_READ, &newstate); +- if (ret != 0) +- return ret; +- if (newstate != state) +- return -ESTALE; +- } ++ ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE); ++ if (ret != 0) ++ return ret; ++ ret = nfs4_open_recover_helper(opendata, FMODE_WRITE); ++ if (ret != 0) ++ return ret; ++ ret = nfs4_open_recover_helper(opendata, FMODE_READ); ++ if (ret != 0) ++ return ret; + /* + * We may have performed cached opens for all three recoveries. + * Check if we need to update the current stateid. +@@ -1749,18 +1748,32 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct + return err; + } + +-int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid) ++int nfs4_open_delegation_recall(struct nfs_open_context *ctx, ++ struct nfs4_state *state, const nfs4_stateid *stateid, ++ fmode_t type) + { + struct nfs_server *server = NFS_SERVER(state->inode); + struct nfs4_opendata *opendata; +- int err; ++ int err = 0; + + opendata = nfs4_open_recoverdata_alloc(ctx, state, + NFS4_OPEN_CLAIM_DELEG_CUR_FH); + if (IS_ERR(opendata)) + return PTR_ERR(opendata); + nfs4_stateid_copy(&opendata->o_arg.u.delegation, stateid); +- err = nfs4_open_recover(opendata, state); ++ clear_bit(NFS_DELEGATED_STATE, &state->flags); ++ switch (type & (FMODE_READ|FMODE_WRITE)) { ++ case FMODE_READ|FMODE_WRITE: ++ case FMODE_WRITE: ++ err = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE); ++ if (err) ++ break; ++ err = nfs4_open_recover_helper(opendata, FMODE_WRITE); ++ if (err) ++ break; ++ case FMODE_READ: ++ err = nfs4_open_recover_helper(opendata, FMODE_READ); ++ } + nfs4_opendata_put(opendata); + return nfs4_handle_delegation_recall_error(server, state, stateid, err); + } +diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c +index 7c5718ba625e..fe3ddd20ff89 100644 +--- a/fs/nfs/pagelist.c ++++ b/fs/nfs/pagelist.c +@@ -508,7 +508,7 @@ size_t nfs_generic_pg_test(struct nfs_pageio_descriptor *desc, + * for it without upsetting the slab allocator. + */ + if (((mirror->pg_count + req->wb_bytes) >> PAGE_SHIFT) * +- sizeof(struct page) > PAGE_SIZE) ++ sizeof(struct page *) > PAGE_SIZE) + return 0; + + return min(mirror->pg_bsize - mirror->pg_count, (size_t)req->wb_bytes); +diff --git a/fs/nfs/read.c b/fs/nfs/read.c +index ae0ff7a11b40..01b8cc8e8cfc 100644 +--- a/fs/nfs/read.c ++++ b/fs/nfs/read.c +@@ -72,6 +72,9 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio) + { + struct nfs_pgio_mirror *mirror; + ++ if (pgio->pg_ops && pgio->pg_ops->pg_cleanup) ++ pgio->pg_ops->pg_cleanup(pgio); ++ + pgio->pg_ops = &nfs_pgio_rw_ops; + + /* read path should never have more than one mirror */ +diff --git a/fs/nfs/write.c b/fs/nfs/write.c +index fdee9270ca15..b45b465bc205 100644 +--- a/fs/nfs/write.c ++++ b/fs/nfs/write.c +@@ -1223,7 +1223,7 @@ static int nfs_can_extend_write(struct file *file, struct page *page, struct ino + return 1; + if (!flctx || (list_empty_careful(&flctx->flc_flock) && + list_empty_careful(&flctx->flc_posix))) +- return 0; ++ return 1; + + /* Check to see if there are whole file write locks */ + ret = 0; +@@ -1351,6 +1351,9 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio) + { + struct nfs_pgio_mirror *mirror; + ++ if (pgio->pg_ops && pgio->pg_ops->pg_cleanup) ++ pgio->pg_ops->pg_cleanup(pgio); ++ + pgio->pg_ops = &nfs_pgio_rw_ops; + + nfs_pageio_stop_mirroring(pgio); +diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c +index fdf4b41d0609..482cfd34472d 100644 +--- a/fs/ocfs2/dlm/dlmmaster.c ++++ b/fs/ocfs2/dlm/dlmmaster.c +@@ -1439,6 +1439,7 @@ int dlm_master_request_handler(struct o2net_msg *msg, u32 len, void *data, + int found, ret; + int set_maybe; + int dispatch_assert = 0; ++ int dispatched = 0; + + if (!dlm_grab(dlm)) + return DLM_MASTER_RESP_NO; +@@ -1658,15 +1659,18 @@ send_response: + mlog(ML_ERROR, "failed to dispatch assert master work\n"); + response = DLM_MASTER_RESP_ERROR; + dlm_lockres_put(res); +- } else ++ } else { ++ dispatched = 1; + __dlm_lockres_grab_inflight_worker(dlm, res); ++ } + spin_unlock(&res->spinlock); + } else { + if (res) + dlm_lockres_put(res); + } + +- dlm_put(dlm); ++ if (!dispatched) ++ dlm_put(dlm); + return response; + } + +@@ -2090,7 +2094,6 @@ int dlm_dispatch_assert_master(struct dlm_ctxt *dlm, + + + /* queue up work for dlm_assert_master_worker */ +- dlm_grab(dlm); /* get an extra ref for the work item */ + dlm_init_work_item(dlm, item, dlm_assert_master_worker, NULL); + item->u.am.lockres = res; /* already have a ref */ + /* can optionally ignore node numbers higher than this node */ +diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c +index ce12e0b1a31f..3d90ad7ff91f 100644 +--- a/fs/ocfs2/dlm/dlmrecovery.c ++++ b/fs/ocfs2/dlm/dlmrecovery.c +@@ -1694,6 +1694,7 @@ int dlm_master_requery_handler(struct o2net_msg *msg, u32 len, void *data, + unsigned int hash; + int master = DLM_LOCK_RES_OWNER_UNKNOWN; + u32 flags = DLM_ASSERT_MASTER_REQUERY; ++ int dispatched = 0; + + if (!dlm_grab(dlm)) { + /* since the domain has gone away on this +@@ -1719,8 +1720,10 @@ int dlm_master_requery_handler(struct o2net_msg *msg, u32 len, void *data, + dlm_put(dlm); + /* sender will take care of this and retry */ + return ret; +- } else ++ } else { ++ dispatched = 1; + __dlm_lockres_grab_inflight_worker(dlm, res); ++ } + spin_unlock(&res->spinlock); + } else { + /* put.. incase we are not the master */ +@@ -1730,7 +1733,8 @@ int dlm_master_requery_handler(struct o2net_msg *msg, u32 len, void *data, + } + spin_unlock(&dlm->spinlock); + +- dlm_put(dlm); ++ if (!dispatched) ++ dlm_put(dlm); + return master; + } + +diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c +index 96f3448b6eb4..fd65b3f1923c 100644 +--- a/fs/ubifs/xattr.c ++++ b/fs/ubifs/xattr.c +@@ -652,11 +652,8 @@ int ubifs_init_security(struct inode *dentry, struct inode *inode, + { + int err; + +- mutex_lock(&inode->i_mutex); + err = security_inode_init_security(inode, dentry, qstr, + &init_xattrs, 0); +- mutex_unlock(&inode->i_mutex); +- + if (err) { + struct ubifs_info *c = dentry->i_sb->s_fs_info; + ubifs_err(c, "cannot initialize security for inode %lu, error %d", +diff --git a/include/asm-generic/preempt.h b/include/asm-generic/preempt.h +index d0a7a4753db2..0bec580a4885 100644 +--- a/include/asm-generic/preempt.h ++++ b/include/asm-generic/preempt.h +@@ -71,9 +71,10 @@ static __always_inline bool __preempt_count_dec_and_test(void) + /* + * Returns true when we need to resched and can (barring IRQ state). + */ +-static __always_inline bool should_resched(void) ++static __always_inline bool should_resched(int preempt_offset) + { +- return unlikely(!preempt_count() && tif_need_resched()); ++ return unlikely(preempt_count() == preempt_offset && ++ tif_need_resched()); + } + + #ifdef CONFIG_PREEMPT +diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h +index 83bfb87f5bf1..e2aadbc7151f 100644 +--- a/include/asm-generic/qspinlock.h ++++ b/include/asm-generic/qspinlock.h +@@ -111,8 +111,8 @@ static inline void queued_spin_unlock_wait(struct qspinlock *lock) + cpu_relax(); + } + +-#ifndef virt_queued_spin_lock +-static __always_inline bool virt_queued_spin_lock(struct qspinlock *lock) ++#ifndef virt_spin_lock ++static __always_inline bool virt_spin_lock(struct qspinlock *lock) + { + return false; + } +diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h +index 93755a629299..430c876ad717 100644 +--- a/include/linux/cgroup-defs.h ++++ b/include/linux/cgroup-defs.h +@@ -463,31 +463,8 @@ struct cgroup_subsys { + unsigned int depends_on; + }; + +-extern struct percpu_rw_semaphore cgroup_threadgroup_rwsem; +- +-/** +- * cgroup_threadgroup_change_begin - threadgroup exclusion for cgroups +- * @tsk: target task +- * +- * Called from threadgroup_change_begin() and allows cgroup operations to +- * synchronize against threadgroup changes using a percpu_rw_semaphore. +- */ +-static inline void cgroup_threadgroup_change_begin(struct task_struct *tsk) +-{ +- percpu_down_read(&cgroup_threadgroup_rwsem); +-} +- +-/** +- * cgroup_threadgroup_change_end - threadgroup exclusion for cgroups +- * @tsk: target task +- * +- * Called from threadgroup_change_end(). Counterpart of +- * cgroup_threadcgroup_change_begin(). +- */ +-static inline void cgroup_threadgroup_change_end(struct task_struct *tsk) +-{ +- percpu_up_read(&cgroup_threadgroup_rwsem); +-} ++void cgroup_threadgroup_change_begin(struct task_struct *tsk); ++void cgroup_threadgroup_change_end(struct task_struct *tsk); + + #else /* CONFIG_CGROUPS */ + +diff --git a/include/linux/init_task.h b/include/linux/init_task.h +index e8493fee8160..bb9b075f0eb0 100644 +--- a/include/linux/init_task.h ++++ b/include/linux/init_task.h +@@ -25,6 +25,13 @@ + extern struct files_struct init_files; + extern struct fs_struct init_fs; + ++#ifdef CONFIG_CGROUPS ++#define INIT_GROUP_RWSEM(sig) \ ++ .group_rwsem = __RWSEM_INITIALIZER(sig.group_rwsem), ++#else ++#define INIT_GROUP_RWSEM(sig) ++#endif ++ + #ifdef CONFIG_CPUSETS + #define INIT_CPUSET_SEQ(tsk) \ + .mems_allowed_seq = SEQCNT_ZERO(tsk.mems_allowed_seq), +@@ -48,6 +55,7 @@ extern struct fs_struct init_fs; + }, \ + .cred_guard_mutex = \ + __MUTEX_INITIALIZER(sig.cred_guard_mutex), \ ++ INIT_GROUP_RWSEM(sig) \ + } + + extern struct nsproxy init_nsproxy; +diff --git a/include/linux/mm.h b/include/linux/mm.h +index bf6f117fcf4d..2b05068f5878 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -916,6 +916,27 @@ static inline void set_page_links(struct page *page, enum zone_type zone, + #endif + } + ++#ifdef CONFIG_MEMCG ++static inline struct mem_cgroup *page_memcg(struct page *page) ++{ ++ return page->mem_cgroup; ++} ++ ++static inline void set_page_memcg(struct page *page, struct mem_cgroup *memcg) ++{ ++ page->mem_cgroup = memcg; ++} ++#else ++static inline struct mem_cgroup *page_memcg(struct page *page) ++{ ++ return NULL; ++} ++ ++static inline void set_page_memcg(struct page *page, struct mem_cgroup *memcg) ++{ ++} ++#endif ++ + /* + * Some inline functions in vmstat.h depend on page_zone() + */ +diff --git a/include/linux/preempt.h b/include/linux/preempt.h +index 84991f185173..bea8dd8ff5e0 100644 +--- a/include/linux/preempt.h ++++ b/include/linux/preempt.h +@@ -84,13 +84,21 @@ + */ + #define in_nmi() (preempt_count() & NMI_MASK) + ++/* ++ * The preempt_count offset after preempt_disable(); ++ */ + #if defined(CONFIG_PREEMPT_COUNT) +-# define PREEMPT_DISABLE_OFFSET 1 ++# define PREEMPT_DISABLE_OFFSET PREEMPT_OFFSET + #else +-# define PREEMPT_DISABLE_OFFSET 0 ++# define PREEMPT_DISABLE_OFFSET 0 + #endif + + /* ++ * The preempt_count offset after spin_lock() ++ */ ++#define PREEMPT_LOCK_OFFSET PREEMPT_DISABLE_OFFSET ++ ++/* + * The preempt_count offset needed for things like: + * + * spin_lock_bh() +@@ -103,7 +111,7 @@ + * + * Work as expected. + */ +-#define SOFTIRQ_LOCK_OFFSET (SOFTIRQ_DISABLE_OFFSET + PREEMPT_DISABLE_OFFSET) ++#define SOFTIRQ_LOCK_OFFSET (SOFTIRQ_DISABLE_OFFSET + PREEMPT_LOCK_OFFSET) + + /* + * Are we running in atomic context? WARNING: this macro cannot +@@ -124,7 +132,8 @@ + #if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER) + extern void preempt_count_add(int val); + extern void preempt_count_sub(int val); +-#define preempt_count_dec_and_test() ({ preempt_count_sub(1); should_resched(); }) ++#define preempt_count_dec_and_test() \ ++ ({ preempt_count_sub(1); should_resched(0); }) + #else + #define preempt_count_add(val) __preempt_count_add(val) + #define preempt_count_sub(val) __preempt_count_sub(val) +@@ -184,7 +193,7 @@ do { \ + + #define preempt_check_resched() \ + do { \ +- if (should_resched()) \ ++ if (should_resched(0)) \ + __preempt_schedule(); \ + } while (0) + +diff --git a/include/linux/sched.h b/include/linux/sched.h +index 04b5ada460b4..bfca8aa215d1 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -754,6 +754,18 @@ struct signal_struct { + unsigned audit_tty_log_passwd; + struct tty_audit_buf *tty_audit_buf; + #endif ++#ifdef CONFIG_CGROUPS ++ /* ++ * group_rwsem prevents new tasks from entering the threadgroup and ++ * member tasks from exiting,a more specifically, setting of ++ * PF_EXITING. fork and exit paths are protected with this rwsem ++ * using threadgroup_change_begin/end(). Users which require ++ * threadgroup to remain stable should use threadgroup_[un]lock() ++ * which also takes care of exec path. Currently, cgroup is the ++ * only user. ++ */ ++ struct rw_semaphore group_rwsem; ++#endif + + oom_flags_t oom_flags; + short oom_score_adj; /* OOM kill score adjustment */ +@@ -2897,12 +2909,6 @@ extern int _cond_resched(void); + + extern int __cond_resched_lock(spinlock_t *lock); + +-#ifdef CONFIG_PREEMPT_COUNT +-#define PREEMPT_LOCK_OFFSET PREEMPT_OFFSET +-#else +-#define PREEMPT_LOCK_OFFSET 0 +-#endif +- + #define cond_resched_lock(lock) ({ \ + ___might_sleep(__FILE__, __LINE__, PREEMPT_LOCK_OFFSET);\ + __cond_resched_lock(lock); \ +diff --git a/include/linux/security.h b/include/linux/security.h +index 79d85ddf8093..2f4c1f7aa7db 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -946,7 +946,7 @@ static inline int security_task_prctl(int option, unsigned long arg2, + unsigned long arg4, + unsigned long arg5) + { +- return cap_task_prctl(option, arg2, arg3, arg3, arg5); ++ return cap_task_prctl(option, arg2, arg3, arg4, arg5); + } + + static inline void security_task_to_inode(struct task_struct *p, struct inode *inode) +diff --git a/include/net/netfilter/br_netfilter.h b/include/net/netfilter/br_netfilter.h +index bab824bde92c..d4c6b5f30acd 100644 +--- a/include/net/netfilter/br_netfilter.h ++++ b/include/net/netfilter/br_netfilter.h +@@ -59,7 +59,7 @@ static inline unsigned int + br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops, struct sk_buff *skb, + const struct nf_hook_state *state) + { +- return NF_DROP; ++ return NF_ACCEPT; + } + #endif + +diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h +index 37cd3911d5c5..4023c4ce260f 100644 +--- a/include/net/netfilter/nf_conntrack.h ++++ b/include/net/netfilter/nf_conntrack.h +@@ -292,6 +292,7 @@ extern unsigned int nf_conntrack_hash_rnd; + void init_nf_conntrack_hash_rnd(void); + + struct nf_conn *nf_ct_tmpl_alloc(struct net *net, u16 zone, gfp_t flags); ++void nf_ct_tmpl_free(struct nf_conn *tmpl); + + #define NF_CT_STAT_INC(net, count) __this_cpu_inc((net)->ct.stat->count) + #define NF_CT_STAT_INC_ATOMIC(net, count) this_cpu_inc((net)->ct.stat->count) +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index 2a246680a6c3..aa8bee72c9d3 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -125,7 +125,7 @@ static inline enum nft_data_types nft_dreg_to_type(enum nft_registers reg) + + static inline enum nft_registers nft_type_to_reg(enum nft_data_types type) + { +- return type == NFT_DATA_VERDICT ? NFT_REG_VERDICT : NFT_REG_1; ++ return type == NFT_DATA_VERDICT ? NFT_REG_VERDICT : NFT_REG_1 * NFT_REG_SIZE / NFT_REG32_SIZE; + } + + unsigned int nft_parse_register(const struct nlattr *attr); +diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h +index 0aedbb2c10e0..7e7f8875ac32 100644 +--- a/include/target/iscsi/iscsi_target_core.h ++++ b/include/target/iscsi/iscsi_target_core.h +@@ -776,7 +776,6 @@ struct iscsi_np { + enum iscsi_timer_flags_table np_login_timer_flags; + u32 np_exports; + enum np_flags_table np_flags; +- unsigned char np_ip[IPV6_ADDRESS_SPACE]; + u16 np_port; + spinlock_t np_thread_lock; + struct completion np_restart_comp; +diff --git a/include/xen/interface/sched.h b/include/xen/interface/sched.h +index 9ce083960a25..f18490985fc8 100644 +--- a/include/xen/interface/sched.h ++++ b/include/xen/interface/sched.h +@@ -107,5 +107,13 @@ struct sched_watchdog { + #define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ + #define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ + #define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */ ++/* ++ * Domain asked to perform 'soft reset' for it. The expected behavior is to ++ * reset internal Xen state for the domain returning it to the point where it ++ * was created but leaving the domain's memory contents and vCPU contexts ++ * intact. This will allow the domain to start over and set up all Xen specific ++ * interfaces again. ++ */ ++#define SHUTDOWN_soft_reset 5 + + #endif /* __XEN_PUBLIC_SCHED_H__ */ +diff --git a/ipc/msg.c b/ipc/msg.c +index 66c4f567eb73..1471db9a7e61 100644 +--- a/ipc/msg.c ++++ b/ipc/msg.c +@@ -137,13 +137,6 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) + return retval; + } + +- /* ipc_addid() locks msq upon success. */ +- id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); +- if (id < 0) { +- ipc_rcu_putref(msq, msg_rcu_free); +- return id; +- } +- + msq->q_stime = msq->q_rtime = 0; + msq->q_ctime = get_seconds(); + msq->q_cbytes = msq->q_qnum = 0; +@@ -153,6 +146,13 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) + INIT_LIST_HEAD(&msq->q_receivers); + INIT_LIST_HEAD(&msq->q_senders); + ++ /* ipc_addid() locks msq upon success. */ ++ id = ipc_addid(&msg_ids(ns), &msq->q_perm, ns->msg_ctlmni); ++ if (id < 0) { ++ ipc_rcu_putref(msq, msg_rcu_free); ++ return id; ++ } ++ + ipc_unlock_object(&msq->q_perm); + rcu_read_unlock(); + +diff --git a/ipc/shm.c b/ipc/shm.c +index 4aef24d91b63..0e61fd430547 100644 +--- a/ipc/shm.c ++++ b/ipc/shm.c +@@ -551,12 +551,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) + if (IS_ERR(file)) + goto no_file; + +- id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); +- if (id < 0) { +- error = id; +- goto no_id; +- } +- + shp->shm_cprid = task_tgid_vnr(current); + shp->shm_lprid = 0; + shp->shm_atim = shp->shm_dtim = 0; +@@ -565,6 +559,13 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) + shp->shm_nattch = 0; + shp->shm_file = file; + shp->shm_creator = current; ++ ++ id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); ++ if (id < 0) { ++ error = id; ++ goto no_id; ++ } ++ + list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); + + /* +diff --git a/ipc/util.c b/ipc/util.c +index be4230020a1f..0f401d94b7c6 100644 +--- a/ipc/util.c ++++ b/ipc/util.c +@@ -237,6 +237,10 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size) + rcu_read_lock(); + spin_lock(&new->lock); + ++ current_euid_egid(&euid, &egid); ++ new->cuid = new->uid = euid; ++ new->gid = new->cgid = egid; ++ + id = idr_alloc(&ids->ipcs_idr, new, + (next_id < 0) ? 0 : ipcid_to_idx(next_id), 0, + GFP_NOWAIT); +@@ -249,10 +253,6 @@ int ipc_addid(struct ipc_ids *ids, struct kern_ipc_perm *new, int size) + + ids->in_use++; + +- current_euid_egid(&euid, &egid); +- new->cuid = new->uid = euid; +- new->gid = new->cgid = egid; +- + if (next_id < 0) { + new->seq = ids->seq++; + if (ids->seq > IPCID_SEQ_MAX) +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index c6c4240e7d28..fe6f855de3d1 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -46,7 +46,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -104,8 +103,6 @@ static DEFINE_SPINLOCK(cgroup_idr_lock); + */ + static DEFINE_SPINLOCK(release_agent_path_lock); + +-struct percpu_rw_semaphore cgroup_threadgroup_rwsem; +- + #define cgroup_assert_mutex_or_rcu_locked() \ + rcu_lockdep_assert(rcu_read_lock_held() || \ + lockdep_is_held(&cgroup_mutex), \ +@@ -870,6 +867,48 @@ static struct css_set *find_css_set(struct css_set *old_cset, + return cset; + } + ++void cgroup_threadgroup_change_begin(struct task_struct *tsk) ++{ ++ down_read(&tsk->signal->group_rwsem); ++} ++ ++void cgroup_threadgroup_change_end(struct task_struct *tsk) ++{ ++ up_read(&tsk->signal->group_rwsem); ++} ++ ++/** ++ * threadgroup_lock - lock threadgroup ++ * @tsk: member task of the threadgroup to lock ++ * ++ * Lock the threadgroup @tsk belongs to. No new task is allowed to enter ++ * and member tasks aren't allowed to exit (as indicated by PF_EXITING) or ++ * change ->group_leader/pid. This is useful for cases where the threadgroup ++ * needs to stay stable across blockable operations. ++ * ++ * fork and exit explicitly call threadgroup_change_{begin|end}() for ++ * synchronization. While held, no new task will be added to threadgroup ++ * and no existing live task will have its PF_EXITING set. ++ * ++ * de_thread() does threadgroup_change_{begin|end}() when a non-leader ++ * sub-thread becomes a new leader. ++ */ ++static void threadgroup_lock(struct task_struct *tsk) ++{ ++ down_write(&tsk->signal->group_rwsem); ++} ++ ++/** ++ * threadgroup_unlock - unlock threadgroup ++ * @tsk: member task of the threadgroup to unlock ++ * ++ * Reverse threadgroup_lock(). ++ */ ++static inline void threadgroup_unlock(struct task_struct *tsk) ++{ ++ up_write(&tsk->signal->group_rwsem); ++} ++ + static struct cgroup_root *cgroup_root_from_kf(struct kernfs_root *kf_root) + { + struct cgroup *root_cgrp = kf_root->kn->priv; +@@ -2066,9 +2105,9 @@ static void cgroup_task_migrate(struct cgroup *old_cgrp, + lockdep_assert_held(&css_set_rwsem); + + /* +- * We are synchronized through cgroup_threadgroup_rwsem against +- * PF_EXITING setting such that we can't race against cgroup_exit() +- * changing the css_set to init_css_set and dropping the old one. ++ * We are synchronized through threadgroup_lock() against PF_EXITING ++ * setting such that we can't race against cgroup_exit() changing the ++ * css_set to init_css_set and dropping the old one. + */ + WARN_ON_ONCE(tsk->flags & PF_EXITING); + old_cset = task_css_set(tsk); +@@ -2125,11 +2164,10 @@ static void cgroup_migrate_finish(struct list_head *preloaded_csets) + * @src_cset and add it to @preloaded_csets, which should later be cleaned + * up by cgroup_migrate_finish(). + * +- * This function may be called without holding cgroup_threadgroup_rwsem +- * even if the target is a process. Threads may be created and destroyed +- * but as long as cgroup_mutex is not dropped, no new css_set can be put +- * into play and the preloaded css_sets are guaranteed to cover all +- * migrations. ++ * This function may be called without holding threadgroup_lock even if the ++ * target is a process. Threads may be created and destroyed but as long ++ * as cgroup_mutex is not dropped, no new css_set can be put into play and ++ * the preloaded css_sets are guaranteed to cover all migrations. + */ + static void cgroup_migrate_add_src(struct css_set *src_cset, + struct cgroup *dst_cgrp, +@@ -2232,7 +2270,7 @@ err: + * @threadgroup: whether @leader points to the whole process or a single task + * + * Migrate a process or task denoted by @leader to @cgrp. If migrating a +- * process, the caller must be holding cgroup_threadgroup_rwsem. The ++ * process, the caller must be holding threadgroup_lock of @leader. The + * caller is also responsible for invoking cgroup_migrate_add_src() and + * cgroup_migrate_prepare_dst() on the targets before invoking this + * function and following up with cgroup_migrate_finish(). +@@ -2360,7 +2398,7 @@ out_release_tset: + * @leader: the task or the leader of the threadgroup to be attached + * @threadgroup: attach the whole threadgroup? + * +- * Call holding cgroup_mutex and cgroup_threadgroup_rwsem. ++ * Call holding cgroup_mutex and threadgroup_lock of @leader. + */ + static int cgroup_attach_task(struct cgroup *dst_cgrp, + struct task_struct *leader, bool threadgroup) +@@ -2452,13 +2490,14 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf, + if (!cgrp) + return -ENODEV; + +- percpu_down_write(&cgroup_threadgroup_rwsem); ++retry_find_task: + rcu_read_lock(); + if (pid) { + tsk = find_task_by_vpid(pid); + if (!tsk) { ++ rcu_read_unlock(); + ret = -ESRCH; +- goto out_unlock_rcu; ++ goto out_unlock_cgroup; + } + } else { + tsk = current; +@@ -2474,23 +2513,37 @@ static ssize_t __cgroup_procs_write(struct kernfs_open_file *of, char *buf, + */ + if (tsk == kthreadd_task || (tsk->flags & PF_NO_SETAFFINITY)) { + ret = -EINVAL; +- goto out_unlock_rcu; ++ rcu_read_unlock(); ++ goto out_unlock_cgroup; + } + + get_task_struct(tsk); + rcu_read_unlock(); + ++ threadgroup_lock(tsk); ++ if (threadgroup) { ++ if (!thread_group_leader(tsk)) { ++ /* ++ * a race with de_thread from another thread's exec() ++ * may strip us of our leadership, if this happens, ++ * there is no choice but to throw this task away and ++ * try again; this is ++ * "double-double-toil-and-trouble-check locking". ++ */ ++ threadgroup_unlock(tsk); ++ put_task_struct(tsk); ++ goto retry_find_task; ++ } ++ } ++ + ret = cgroup_procs_write_permission(tsk, cgrp, of); + if (!ret) + ret = cgroup_attach_task(cgrp, tsk, threadgroup); + +- put_task_struct(tsk); +- goto out_unlock_threadgroup; ++ threadgroup_unlock(tsk); + +-out_unlock_rcu: +- rcu_read_unlock(); +-out_unlock_threadgroup: +- percpu_up_write(&cgroup_threadgroup_rwsem); ++ put_task_struct(tsk); ++out_unlock_cgroup: + cgroup_kn_unlock(of->kn); + return ret ?: nbytes; + } +@@ -2635,8 +2688,6 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp) + + lockdep_assert_held(&cgroup_mutex); + +- percpu_down_write(&cgroup_threadgroup_rwsem); +- + /* look up all csses currently attached to @cgrp's subtree */ + down_read(&css_set_rwsem); + css_for_each_descendant_pre(css, cgroup_css(cgrp, NULL)) { +@@ -2692,8 +2743,17 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp) + goto out_finish; + last_task = task; + ++ threadgroup_lock(task); ++ /* raced against de_thread() from another thread? */ ++ if (!thread_group_leader(task)) { ++ threadgroup_unlock(task); ++ put_task_struct(task); ++ continue; ++ } ++ + ret = cgroup_migrate(src_cset->dfl_cgrp, task, true); + ++ threadgroup_unlock(task); + put_task_struct(task); + + if (WARN(ret, "cgroup: failed to update controllers for the default hierarchy (%d), further operations may crash or hang\n", ret)) +@@ -2703,7 +2763,6 @@ static int cgroup_update_dfl_csses(struct cgroup *cgrp) + + out_finish: + cgroup_migrate_finish(&preloaded_csets); +- percpu_up_write(&cgroup_threadgroup_rwsem); + return ret; + } + +@@ -5013,7 +5072,6 @@ int __init cgroup_init(void) + unsigned long key; + int ssid, err; + +- BUG_ON(percpu_init_rwsem(&cgroup_threadgroup_rwsem)); + BUG_ON(cgroup_init_cftypes(NULL, cgroup_dfl_base_files)); + BUG_ON(cgroup_init_cftypes(NULL, cgroup_legacy_base_files)); + +diff --git a/kernel/fork.c b/kernel/fork.c +index 26a70dc7a915..e769c8c86f86 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1146,6 +1146,10 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) + tty_audit_fork(sig); + sched_autogroup_fork(sig); + ++#ifdef CONFIG_CGROUPS ++ init_rwsem(&sig->group_rwsem); ++#endif ++ + sig->oom_score_adj = current->signal->oom_score_adj; + sig->oom_score_adj_min = current->signal->oom_score_adj_min; + +diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c +index 0e97c142ce40..4e6267a34440 100644 +--- a/kernel/irq/proc.c ++++ b/kernel/irq/proc.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + + #include "internals.h" + +@@ -323,18 +324,29 @@ void register_handler_proc(unsigned int irq, struct irqaction *action) + + void register_irq_proc(unsigned int irq, struct irq_desc *desc) + { ++ static DEFINE_MUTEX(register_lock); + char name [MAX_NAMELEN]; + +- if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip) || desc->dir) ++ if (!root_irq_dir || (desc->irq_data.chip == &no_irq_chip)) + return; + ++ /* ++ * irq directories are registered only when a handler is ++ * added, not when the descriptor is created, so multiple ++ * tasks might try to register at the same time. ++ */ ++ mutex_lock(®ister_lock); ++ ++ if (desc->dir) ++ goto out_unlock; ++ + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + desc->dir = proc_mkdir(name, root_irq_dir); + if (!desc->dir) +- return; ++ goto out_unlock; + + #ifdef CONFIG_SMP + /* create /proc/irq//smp_affinity */ +@@ -355,6 +367,9 @@ void register_irq_proc(unsigned int irq, struct irq_desc *desc) + + proc_create_data("spurious", 0444, desc->dir, + &irq_spurious_proc_fops, (void *)(long)irq); ++ ++out_unlock: ++ mutex_unlock(®ister_lock); + } + + void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) +diff --git a/kernel/locking/qspinlock.c b/kernel/locking/qspinlock.c +index 38c49202d532..8ed01611ae73 100644 +--- a/kernel/locking/qspinlock.c ++++ b/kernel/locking/qspinlock.c +@@ -289,7 +289,7 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val) + if (pv_enabled()) + goto queue; + +- if (virt_queued_spin_lock(lock)) ++ if (virt_spin_lock(lock)) + return; + + /* +diff --git a/kernel/sched/core.c b/kernel/sched/core.c +index e9673433cc01..6776631676e0 100644 +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -2461,11 +2461,11 @@ static struct rq *finish_task_switch(struct task_struct *prev) + * If a task dies, then it sets TASK_DEAD in tsk->state and calls + * schedule one last time. The schedule call will never return, and + * the scheduled task must drop that reference. +- * The test for TASK_DEAD must occur while the runqueue locks are +- * still held, otherwise prev could be scheduled on another cpu, die +- * there before we look at prev->state, and then the reference would +- * be dropped twice. +- * Manfred Spraul ++ * ++ * We must observe prev->state before clearing prev->on_cpu (in ++ * finish_lock_switch), otherwise a concurrent wakeup can get prev ++ * running on another CPU and we could rave with its RUNNING -> DEAD ++ * transition, resulting in a double drop. + */ + prev_state = prev->state; + vtime_task_switch(prev); +@@ -2614,13 +2614,20 @@ unsigned long nr_running(void) + + /* + * Check if only the current task is running on the cpu. ++ * ++ * Caution: this function does not check that the caller has disabled ++ * preemption, thus the result might have a time-of-check-to-time-of-use ++ * race. The caller is responsible to use it correctly, for example: ++ * ++ * - from a non-preemptable section (of course) ++ * ++ * - from a thread that is bound to a single CPU ++ * ++ * - in a loop with very short iterations (e.g. a polling loop) + */ + bool single_task_running(void) + { +- if (cpu_rq(smp_processor_id())->nr_running == 1) +- return true; +- else +- return false; ++ return raw_rq()->nr_running == 1; + } + EXPORT_SYMBOL(single_task_running); + +@@ -4492,7 +4499,7 @@ SYSCALL_DEFINE0(sched_yield) + + int __sched _cond_resched(void) + { +- if (should_resched()) { ++ if (should_resched(0)) { + preempt_schedule_common(); + return 1; + } +@@ -4510,7 +4517,7 @@ EXPORT_SYMBOL(_cond_resched); + */ + int __cond_resched_lock(spinlock_t *lock) + { +- int resched = should_resched(); ++ int resched = should_resched(PREEMPT_LOCK_OFFSET); + int ret = 0; + + lockdep_assert_held(lock); +@@ -4532,7 +4539,7 @@ int __sched __cond_resched_softirq(void) + { + BUG_ON(!in_softirq()); + +- if (should_resched()) { ++ if (should_resched(SOFTIRQ_DISABLE_OFFSET)) { + local_bh_enable(); + preempt_schedule_common(); + local_bh_disable(); +diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h +index 84d48790bb6d..08ab96b366bf 100644 +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -1091,9 +1091,10 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) + * After ->on_cpu is cleared, the task can be moved to a different CPU. + * We must ensure this doesn't happen until the switch is completely + * finished. ++ * ++ * Pairs with the control dependency and rmb in try_to_wake_up(). + */ +- smp_wmb(); +- prev->on_cpu = 0; ++ smp_store_release(&prev->on_cpu, 0); + #endif + #ifdef CONFIG_DEBUG_SPINLOCK + /* this is a valid case when another task releases the spinlock */ +diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c +index 841b72f720e8..3a38775b50c2 100644 +--- a/kernel/time/clocksource.c ++++ b/kernel/time/clocksource.c +@@ -217,7 +217,7 @@ static void clocksource_watchdog(unsigned long data) + continue; + + /* Check the deviation from the watchdog clocksource. */ +- if ((abs(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD)) { ++ if (abs64(cs_nsec - wd_nsec) > WATCHDOG_THRESHOLD) { + pr_warn("timekeeping watchdog: Marking clocksource '%s' as unstable because the skew is too large:\n", + cs->name); + pr_warn(" '%s' wd_now: %llx wd_last: %llx mask: %llx\n", +diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c +index bca3667a2de1..a20d4110e871 100644 +--- a/kernel/time/timekeeping.c ++++ b/kernel/time/timekeeping.c +@@ -1607,7 +1607,7 @@ static __always_inline void timekeeping_freqadjust(struct timekeeper *tk, + negative = (tick_error < 0); + + /* Sort out the magnitude of the correction */ +- tick_error = abs(tick_error); ++ tick_error = abs64(tick_error); + for (adj = 0; tick_error > interval; adj++) + tick_error >>= 1; + +diff --git a/lib/iommu-common.c b/lib/iommu-common.c +index ff19f66d3f7f..b1c93e94ca7a 100644 +--- a/lib/iommu-common.c ++++ b/lib/iommu-common.c +@@ -21,8 +21,7 @@ static DEFINE_PER_CPU(unsigned int, iommu_hash_common); + + static inline bool need_flush(struct iommu_map_table *iommu) + { +- return (iommu->lazy_flush != NULL && +- (iommu->flags & IOMMU_NEED_FLUSH) != 0); ++ return ((iommu->flags & IOMMU_NEED_FLUSH) != 0); + } + + static inline void set_flush(struct iommu_map_table *iommu) +@@ -211,7 +210,8 @@ unsigned long iommu_tbl_range_alloc(struct device *dev, + goto bail; + } + } +- if (n < pool->hint || need_flush(iommu)) { ++ if (iommu->lazy_flush && ++ (n < pool->hint || need_flush(iommu))) { + clear_flush(iommu); + iommu->lazy_flush(iommu); + } +diff --git a/mm/hugetlb.c b/mm/hugetlb.c +index a8c3087089d8..62c1ec5a9d31 100644 +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -2974,6 +2974,14 @@ static void unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, + continue; + + /* ++ * Shared VMAs have their own reserves and do not affect ++ * MAP_PRIVATE accounting but it is possible that a shared ++ * VMA is using the same page so check and skip such VMAs. ++ */ ++ if (iter_vma->vm_flags & VM_MAYSHARE) ++ continue; ++ ++ /* + * Unmap the page from other VMAs without their own reserves. + * They get marked to be SIGKILLed if they fault in these + * areas. This is because a future no-page fault on this VMA +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index acb93c554f6e..237d4686482d 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -806,12 +806,14 @@ mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_zone *mctz) + } + + /* ++ * Return page count for single (non recursive) @memcg. ++ * + * Implementation Note: reading percpu statistics for memcg. + * + * Both of vmstat[] and percpu_counter has threshold and do periodic + * synchronization to implement "quick" read. There are trade-off between + * reading cost and precision of value. Then, we may have a chance to implement +- * a periodic synchronizion of counter in memcg's counter. ++ * a periodic synchronization of counter in memcg's counter. + * + * But this _read() function is used for user interface now. The user accounts + * memory usage by memory cgroup and he _always_ requires exact value because +@@ -821,17 +823,24 @@ mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_zone *mctz) + * + * If there are kernel internal actions which can make use of some not-exact + * value, and reading all cpu value can be performance bottleneck in some +- * common workload, threashold and synchonization as vmstat[] should be ++ * common workload, threshold and synchronization as vmstat[] should be + * implemented. + */ +-static long mem_cgroup_read_stat(struct mem_cgroup *memcg, +- enum mem_cgroup_stat_index idx) ++static unsigned long ++mem_cgroup_read_stat(struct mem_cgroup *memcg, enum mem_cgroup_stat_index idx) + { + long val = 0; + int cpu; + ++ /* Per-cpu values can be negative, use a signed accumulator */ + for_each_possible_cpu(cpu) + val += per_cpu(memcg->stat->count[idx], cpu); ++ /* ++ * Summing races with updates, so val may be negative. Avoid exposing ++ * transient negative values. ++ */ ++ if (val < 0) ++ val = 0; + return val; + } + +@@ -1498,7 +1507,7 @@ void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p) + for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) { + if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account) + continue; +- pr_cont(" %s:%ldKB", mem_cgroup_stat_names[i], ++ pr_cont(" %s:%luKB", mem_cgroup_stat_names[i], + K(mem_cgroup_read_stat(iter, i))); + } + +@@ -3119,14 +3128,11 @@ static unsigned long tree_stat(struct mem_cgroup *memcg, + enum mem_cgroup_stat_index idx) + { + struct mem_cgroup *iter; +- long val = 0; ++ unsigned long val = 0; + +- /* Per-cpu values can be negative, use a signed accumulator */ + for_each_mem_cgroup_tree(iter, memcg) + val += mem_cgroup_read_stat(iter, idx); + +- if (val < 0) /* race ? */ +- val = 0; + return val; + } + +@@ -3469,7 +3475,7 @@ static int memcg_stat_show(struct seq_file *m, void *v) + for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) { + if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account) + continue; +- seq_printf(m, "%s %ld\n", mem_cgroup_stat_names[i], ++ seq_printf(m, "%s %lu\n", mem_cgroup_stat_names[i], + mem_cgroup_read_stat(memcg, i) * PAGE_SIZE); + } + +@@ -3494,13 +3500,13 @@ static int memcg_stat_show(struct seq_file *m, void *v) + (u64)memsw * PAGE_SIZE); + + for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) { +- long long val = 0; ++ unsigned long long val = 0; + + if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account) + continue; + for_each_mem_cgroup_tree(mi, memcg) + val += mem_cgroup_read_stat(mi, i) * PAGE_SIZE; +- seq_printf(m, "total_%s %lld\n", mem_cgroup_stat_names[i], val); ++ seq_printf(m, "total_%s %llu\n", mem_cgroup_stat_names[i], val); + } + + for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++) { +diff --git a/mm/migrate.c b/mm/migrate.c +index eb4267107d1f..fcb6204de108 100644 +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -734,6 +734,15 @@ static int move_to_new_page(struct page *newpage, struct page *page, + if (PageSwapBacked(page)) + SetPageSwapBacked(newpage); + ++ /* ++ * Indirectly called below, migrate_page_copy() copies PG_dirty and thus ++ * needs newpage's memcg set to transfer memcg dirty page accounting. ++ * So perform memcg migration in two steps: ++ * 1. set newpage->mem_cgroup (here) ++ * 2. clear page->mem_cgroup (below) ++ */ ++ set_page_memcg(newpage, page_memcg(page)); ++ + mapping = page_mapping(page); + if (!mapping) + rc = migrate_page(mapping, newpage, page, mode); +@@ -750,9 +759,10 @@ static int move_to_new_page(struct page *newpage, struct page *page, + rc = fallback_migrate_page(mapping, newpage, page, mode); + + if (rc != MIGRATEPAGE_SUCCESS) { ++ set_page_memcg(newpage, NULL); + newpage->mapping = NULL; + } else { +- mem_cgroup_migrate(page, newpage, false); ++ set_page_memcg(page, NULL); + if (page_was_mapped) + remove_migration_ptes(page, newpage); + page->mapping = NULL; +@@ -1068,7 +1078,7 @@ out: + if (rc != MIGRATEPAGE_SUCCESS && put_new_page) + put_new_page(new_hpage, private); + else +- put_page(new_hpage); ++ putback_active_hugepage(new_hpage); + + if (result) { + if (rc) +diff --git a/mm/slab.c b/mm/slab.c +index bbd0b47dc6a9..ae360283029c 100644 +--- a/mm/slab.c ++++ b/mm/slab.c +@@ -2190,9 +2190,16 @@ __kmem_cache_create (struct kmem_cache *cachep, unsigned long flags) + size += BYTES_PER_WORD; + } + #if FORCED_DEBUG && defined(CONFIG_DEBUG_PAGEALLOC) +- if (size >= kmalloc_size(INDEX_NODE + 1) +- && cachep->object_size > cache_line_size() +- && ALIGN(size, cachep->align) < PAGE_SIZE) { ++ /* ++ * To activate debug pagealloc, off-slab management is necessary ++ * requirement. In early phase of initialization, small sized slab ++ * doesn't get initialized so it would not be possible. So, we need ++ * to check size >= 256. It guarantees that all necessary small ++ * sized slab is initialized in current slab initialization sequence. ++ */ ++ if (!slab_early_init && size >= kmalloc_size(INDEX_NODE) && ++ size >= 256 && cachep->object_size > cache_line_size() && ++ ALIGN(size, cachep->align) < PAGE_SIZE) { + cachep->obj_offset += PAGE_SIZE - ALIGN(size, cachep->align); + size = PAGE_SIZE; + } +diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c +index 6d0b471eede8..cc7d87d64987 100644 +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -19,6 +19,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -453,7 +454,7 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, + int j; + + /* check if orig node candidate is running DAT */ +- if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT)) ++ if (!test_bit(BATADV_ORIG_CAPA_HAS_DAT, &candidate->capabilities)) + goto out; + + /* Check if this node has already been selected... */ +@@ -713,9 +714,9 @@ static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + uint16_t tvlv_value_len) + { + if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) +- orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT; ++ clear_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities); + else +- orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT; ++ set_bit(BATADV_ORIG_CAPA_HAS_DAT, &orig->capabilities); + } + + /** +diff --git a/net/batman-adv/multicast.c b/net/batman-adv/multicast.c +index 7aa480b7edd0..68a9554961eb 100644 +--- a/net/batman-adv/multicast.c ++++ b/net/batman-adv/multicast.c +@@ -19,6 +19,8 @@ + #include "main.h" + + #include ++#include ++#include + #include + #include + #include +@@ -588,19 +590,26 @@ batadv_mcast_forw_mode(struct batadv_priv *bat_priv, struct sk_buff *skb, + * + * If the BATADV_MCAST_WANT_ALL_UNSNOOPABLES flag of this originator, + * orig, has toggled then this method updates counter and list accordingly. ++ * ++ * Caller needs to hold orig->mcast_handler_lock. + */ + static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t mcast_flags) + { ++ struct hlist_node *node = &orig->mcast_want_all_unsnoopables_node; ++ struct hlist_head *head = &bat_priv->mcast.want_all_unsnoopables_list; ++ + /* switched from flag unset to set */ + if (mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES)) { + atomic_inc(&bat_priv->mcast.num_want_all_unsnoopables); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); +- hlist_add_head_rcu(&orig->mcast_want_all_unsnoopables_node, +- &bat_priv->mcast.want_all_unsnoopables_list); ++ /* flag checks above + mcast_handler_lock prevents this */ ++ WARN_ON(!hlist_unhashed(node)); ++ ++ hlist_add_head_rcu(node, head); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + /* switched from flag set to unset */ + } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_UNSNOOPABLES) && +@@ -608,7 +617,10 @@ static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv, + atomic_dec(&bat_priv->mcast.num_want_all_unsnoopables); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); +- hlist_del_rcu(&orig->mcast_want_all_unsnoopables_node); ++ /* flag checks above + mcast_handler_lock prevents this */ ++ WARN_ON(hlist_unhashed(node)); ++ ++ hlist_del_init_rcu(node); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + } + } +@@ -621,19 +633,26 @@ static void batadv_mcast_want_unsnoop_update(struct batadv_priv *bat_priv, + * + * If the BATADV_MCAST_WANT_ALL_IPV4 flag of this originator, orig, has + * toggled then this method updates counter and list accordingly. ++ * ++ * Caller needs to hold orig->mcast_handler_lock. + */ + static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t mcast_flags) + { ++ struct hlist_node *node = &orig->mcast_want_all_ipv4_node; ++ struct hlist_head *head = &bat_priv->mcast.want_all_ipv4_list; ++ + /* switched from flag unset to set */ + if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV4 && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV4)) { + atomic_inc(&bat_priv->mcast.num_want_all_ipv4); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); +- hlist_add_head_rcu(&orig->mcast_want_all_ipv4_node, +- &bat_priv->mcast.want_all_ipv4_list); ++ /* flag checks above + mcast_handler_lock prevents this */ ++ WARN_ON(!hlist_unhashed(node)); ++ ++ hlist_add_head_rcu(node, head); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + /* switched from flag set to unset */ + } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV4) && +@@ -641,7 +660,10 @@ static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv, + atomic_dec(&bat_priv->mcast.num_want_all_ipv4); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); +- hlist_del_rcu(&orig->mcast_want_all_ipv4_node); ++ /* flag checks above + mcast_handler_lock prevents this */ ++ WARN_ON(hlist_unhashed(node)); ++ ++ hlist_del_init_rcu(node); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + } + } +@@ -654,19 +676,26 @@ static void batadv_mcast_want_ipv4_update(struct batadv_priv *bat_priv, + * + * If the BATADV_MCAST_WANT_ALL_IPV6 flag of this originator, orig, has + * toggled then this method updates counter and list accordingly. ++ * ++ * Caller needs to hold orig->mcast_handler_lock. + */ + static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t mcast_flags) + { ++ struct hlist_node *node = &orig->mcast_want_all_ipv6_node; ++ struct hlist_head *head = &bat_priv->mcast.want_all_ipv6_list; ++ + /* switched from flag unset to set */ + if (mcast_flags & BATADV_MCAST_WANT_ALL_IPV6 && + !(orig->mcast_flags & BATADV_MCAST_WANT_ALL_IPV6)) { + atomic_inc(&bat_priv->mcast.num_want_all_ipv6); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); +- hlist_add_head_rcu(&orig->mcast_want_all_ipv6_node, +- &bat_priv->mcast.want_all_ipv6_list); ++ /* flag checks above + mcast_handler_lock prevents this */ ++ WARN_ON(!hlist_unhashed(node)); ++ ++ hlist_add_head_rcu(node, head); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + /* switched from flag set to unset */ + } else if (!(mcast_flags & BATADV_MCAST_WANT_ALL_IPV6) && +@@ -674,7 +703,10 @@ static void batadv_mcast_want_ipv6_update(struct batadv_priv *bat_priv, + atomic_dec(&bat_priv->mcast.num_want_all_ipv6); + + spin_lock_bh(&bat_priv->mcast.want_lists_lock); +- hlist_del_rcu(&orig->mcast_want_all_ipv6_node); ++ /* flag checks above + mcast_handler_lock prevents this */ ++ WARN_ON(hlist_unhashed(node)); ++ ++ hlist_del_init_rcu(node); + spin_unlock_bh(&bat_priv->mcast.want_lists_lock); + } + } +@@ -697,39 +729,42 @@ static void batadv_mcast_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + uint8_t mcast_flags = BATADV_NO_FLAGS; + bool orig_initialized; + +- orig_initialized = orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST; ++ if (orig_mcast_enabled && tvlv_value && ++ (tvlv_value_len >= sizeof(mcast_flags))) ++ mcast_flags = *(uint8_t *)tvlv_value; ++ ++ spin_lock_bh(&orig->mcast_handler_lock); ++ orig_initialized = test_bit(BATADV_ORIG_CAPA_HAS_MCAST, ++ &orig->capa_initialized); + + /* If mcast support is turned on decrease the disabled mcast node + * counter only if we had increased it for this node before. If this + * is a completely new orig_node no need to decrease the counter. + */ + if (orig_mcast_enabled && +- !(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST)) { ++ !test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities)) { + if (orig_initialized) + atomic_dec(&bat_priv->mcast.num_disabled); +- orig->capabilities |= BATADV_ORIG_CAPA_HAS_MCAST; ++ set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities); + /* If mcast support is being switched off or if this is an initial + * OGM without mcast support then increase the disabled mcast + * node counter. + */ + } else if (!orig_mcast_enabled && +- (orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST || ++ (test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities) || + !orig_initialized)) { + atomic_inc(&bat_priv->mcast.num_disabled); +- orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_MCAST; ++ clear_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities); + } + +- orig->capa_initialized |= BATADV_ORIG_CAPA_HAS_MCAST; +- +- if (orig_mcast_enabled && tvlv_value && +- (tvlv_value_len >= sizeof(mcast_flags))) +- mcast_flags = *(uint8_t *)tvlv_value; ++ set_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capa_initialized); + + batadv_mcast_want_unsnoop_update(bat_priv, orig, mcast_flags); + batadv_mcast_want_ipv4_update(bat_priv, orig, mcast_flags); + batadv_mcast_want_ipv6_update(bat_priv, orig, mcast_flags); + + orig->mcast_flags = mcast_flags; ++ spin_unlock_bh(&orig->mcast_handler_lock); + } + + /** +@@ -763,11 +798,15 @@ void batadv_mcast_purge_orig(struct batadv_orig_node *orig) + { + struct batadv_priv *bat_priv = orig->bat_priv; + +- if (!(orig->capabilities & BATADV_ORIG_CAPA_HAS_MCAST) && +- orig->capa_initialized & BATADV_ORIG_CAPA_HAS_MCAST) ++ spin_lock_bh(&orig->mcast_handler_lock); ++ ++ if (!test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capabilities) && ++ test_bit(BATADV_ORIG_CAPA_HAS_MCAST, &orig->capa_initialized)) + atomic_dec(&bat_priv->mcast.num_disabled); + + batadv_mcast_want_unsnoop_update(bat_priv, orig, BATADV_NO_FLAGS); + batadv_mcast_want_ipv4_update(bat_priv, orig, BATADV_NO_FLAGS); + batadv_mcast_want_ipv6_update(bat_priv, orig, BATADV_NO_FLAGS); ++ ++ spin_unlock_bh(&orig->mcast_handler_lock); + } +diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c +index f0a50f31d822..46604010dcd4 100644 +--- a/net/batman-adv/network-coding.c ++++ b/net/batman-adv/network-coding.c +@@ -19,6 +19,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -134,9 +135,9 @@ static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + uint16_t tvlv_value_len) + { + if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) +- orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_NC; ++ clear_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities); + else +- orig->capabilities |= BATADV_ORIG_CAPA_HAS_NC; ++ set_bit(BATADV_ORIG_CAPA_HAS_NC, &orig->capabilities); + } + + /** +@@ -894,7 +895,7 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv, + goto out; + + /* check if orig node is network coding enabled */ +- if (!(orig_node->capabilities & BATADV_ORIG_CAPA_HAS_NC)) ++ if (!test_bit(BATADV_ORIG_CAPA_HAS_NC, &orig_node->capabilities)) + goto out; + + /* accept ogms from 'good' neighbors and single hop neighbors */ +diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c +index 018b7495ad84..32a0fcfab36d 100644 +--- a/net/batman-adv/originator.c ++++ b/net/batman-adv/originator.c +@@ -696,8 +696,13 @@ struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, + orig_node->last_seen = jiffies; + reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); + orig_node->bcast_seqno_reset = reset_time; ++ + #ifdef CONFIG_BATMAN_ADV_MCAST + orig_node->mcast_flags = BATADV_NO_FLAGS; ++ INIT_HLIST_NODE(&orig_node->mcast_want_all_unsnoopables_node); ++ INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv4_node); ++ INIT_HLIST_NODE(&orig_node->mcast_want_all_ipv6_node); ++ spin_lock_init(&orig_node->mcast_handler_lock); + #endif + + /* create a vlan object for the "untagged" LAN */ +diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c +index a2fc843c2243..51cda3a7c51d 100644 +--- a/net/batman-adv/soft-interface.c ++++ b/net/batman-adv/soft-interface.c +@@ -202,6 +202,7 @@ static int batadv_interface_tx(struct sk_buff *skb, + int gw_mode; + enum batadv_forw_mode forw_mode; + struct batadv_orig_node *mcast_single_orig = NULL; ++ int network_offset = ETH_HLEN; + + if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + goto dropped; +@@ -214,14 +215,18 @@ static int batadv_interface_tx(struct sk_buff *skb, + case ETH_P_8021Q: + vhdr = vlan_eth_hdr(skb); + +- if (vhdr->h_vlan_encapsulated_proto != ethertype) ++ if (vhdr->h_vlan_encapsulated_proto != ethertype) { ++ network_offset += VLAN_HLEN; + break; ++ } + + /* fall through */ + case ETH_P_BATMAN: + goto dropped; + } + ++ skb_set_network_header(skb, network_offset); ++ + if (batadv_bla_tx(bat_priv, skb, vid)) + goto dropped; + +diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c +index 5809b39c1922..c9b26291ac4c 100644 +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -19,6 +19,7 @@ + #include "main.h" + + #include ++#include + #include + #include + #include +@@ -1882,7 +1883,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, + } + spin_unlock_bh(list_lock); + } +- orig_node->capa_initialized &= ~BATADV_ORIG_CAPA_HAS_TT; ++ clear_bit(BATADV_ORIG_CAPA_HAS_TT, &orig_node->capa_initialized); + } + + static bool batadv_tt_global_to_purge(struct batadv_tt_global_entry *tt_global, +@@ -2841,7 +2842,7 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, + return; + } + } +- orig_node->capa_initialized |= BATADV_ORIG_CAPA_HAS_TT; ++ set_bit(BATADV_ORIG_CAPA_HAS_TT, &orig_node->capa_initialized); + } + + static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, +@@ -3343,7 +3344,8 @@ static void batadv_tt_update_orig(struct batadv_priv *bat_priv, + bool has_tt_init; + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff; +- has_tt_init = orig_node->capa_initialized & BATADV_ORIG_CAPA_HAS_TT; ++ has_tt_init = test_bit(BATADV_ORIG_CAPA_HAS_TT, ++ &orig_node->capa_initialized); + + /* orig table not initialised AND first diff is in the OGM OR the ttvn + * increased by one -> we can apply the attached changes +diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h +index 67d63483618e..55610a805b53 100644 +--- a/net/batman-adv/types.h ++++ b/net/batman-adv/types.h +@@ -221,6 +221,7 @@ struct batadv_orig_bat_iv { + * @batadv_dat_addr_t: address of the orig node in the distributed hash + * @last_seen: time when last packet from this node was received + * @bcast_seqno_reset: time when the broadcast seqno window was reset ++ * @mcast_handler_lock: synchronizes mcast-capability and -flag changes + * @mcast_flags: multicast flags announced by the orig node + * @mcast_want_all_unsnoop_node: a list node for the + * mcast.want_all_unsnoopables list +@@ -268,13 +269,15 @@ struct batadv_orig_node { + unsigned long last_seen; + unsigned long bcast_seqno_reset; + #ifdef CONFIG_BATMAN_ADV_MCAST ++ /* synchronizes mcast tvlv specific orig changes */ ++ spinlock_t mcast_handler_lock; + uint8_t mcast_flags; + struct hlist_node mcast_want_all_unsnoopables_node; + struct hlist_node mcast_want_all_ipv4_node; + struct hlist_node mcast_want_all_ipv6_node; + #endif +- uint8_t capabilities; +- uint8_t capa_initialized; ++ unsigned long capabilities; ++ unsigned long capa_initialized; + atomic_t last_ttvn; + unsigned char *tt_buff; + int16_t tt_buff_len; +@@ -313,10 +316,10 @@ struct batadv_orig_node { + * (= orig node announces a tvlv of type BATADV_TVLV_MCAST) + */ + enum batadv_orig_capabilities { +- BATADV_ORIG_CAPA_HAS_DAT = BIT(0), +- BATADV_ORIG_CAPA_HAS_NC = BIT(1), +- BATADV_ORIG_CAPA_HAS_TT = BIT(2), +- BATADV_ORIG_CAPA_HAS_MCAST = BIT(3), ++ BATADV_ORIG_CAPA_HAS_DAT, ++ BATADV_ORIG_CAPA_HAS_NC, ++ BATADV_ORIG_CAPA_HAS_TT, ++ BATADV_ORIG_CAPA_HAS_MCAST, + }; + + /** +diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c +index ad82324f710f..0510a577a7b5 100644 +--- a/net/bluetooth/smp.c ++++ b/net/bluetooth/smp.c +@@ -2311,12 +2311,6 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) + if (!conn) + return 1; + +- chan = conn->smp; +- if (!chan) { +- BT_ERR("SMP security requested but not available"); +- return 1; +- } +- + if (!hci_dev_test_flag(hcon->hdev, HCI_LE_ENABLED)) + return 1; + +@@ -2330,6 +2324,12 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) + if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) + return 0; + ++ chan = conn->smp; ++ if (!chan) { ++ BT_ERR("SMP security requested but not available"); ++ return 1; ++ } ++ + l2cap_chan_lock(chan); + + /* If SMP is already in progress ignore this request */ +diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h +index afe905c208af..691b54fcaf2a 100644 +--- a/net/netfilter/ipset/ip_set_hash_gen.h ++++ b/net/netfilter/ipset/ip_set_hash_gen.h +@@ -152,9 +152,13 @@ htable_bits(u32 hashsize) + #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) + + #ifdef IP_SET_HASH_WITH_NET0 ++/* cidr from 0 to SET_HOST_MASK() value and c = cidr + 1 */ + #define NLEN(family) (SET_HOST_MASK(family) + 1) ++#define CIDR_POS(c) ((c) - 1) + #else ++/* cidr from 1 to SET_HOST_MASK() value and c = cidr + 1 */ + #define NLEN(family) SET_HOST_MASK(family) ++#define CIDR_POS(c) ((c) - 2) + #endif + + #else +@@ -305,7 +309,7 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) + } else if (h->nets[i].cidr[n] < cidr) { + j = i; + } else if (h->nets[i].cidr[n] == cidr) { +- h->nets[cidr - 1].nets[n]++; ++ h->nets[CIDR_POS(cidr)].nets[n]++; + return; + } + } +@@ -314,7 +318,7 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) + h->nets[i].cidr[n] = h->nets[i - 1].cidr[n]; + } + h->nets[i].cidr[n] = cidr; +- h->nets[cidr - 1].nets[n] = 1; ++ h->nets[CIDR_POS(cidr)].nets[n] = 1; + } + + static void +@@ -325,8 +329,8 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) + for (i = 0; i < nets_length; i++) { + if (h->nets[i].cidr[n] != cidr) + continue; +- h->nets[cidr - 1].nets[n]--; +- if (h->nets[cidr - 1].nets[n] > 0) ++ h->nets[CIDR_POS(cidr)].nets[n]--; ++ if (h->nets[CIDR_POS(cidr)].nets[n] > 0) + return; + for (j = i; j < net_end && h->nets[j].cidr[n]; j++) + h->nets[j].cidr[n] = h->nets[j + 1].cidr[n]; +diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c +index 3c862c0a76d1..a93dfebffa81 100644 +--- a/net/netfilter/ipset/ip_set_hash_netnet.c ++++ b/net/netfilter/ipset/ip_set_hash_netnet.c +@@ -131,6 +131,13 @@ hash_netnet4_data_next(struct hash_netnet4_elem *next, + #define HOST_MASK 32 + #include "ip_set_hash_gen.h" + ++static void ++hash_netnet4_init(struct hash_netnet4_elem *e) ++{ ++ e->cidr[0] = HOST_MASK; ++ e->cidr[1] = HOST_MASK; ++} ++ + static int + hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, +@@ -160,7 +167,7 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], + { + const struct hash_netnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; +- struct hash_netnet4_elem e = { .cidr = { HOST_MASK, HOST_MASK, }, }; ++ struct hash_netnet4_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, last; + u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2; +@@ -169,6 +176,7 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + ++ hash_netnet4_init(&e); + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) + return -IPSET_ERR_PROTOCOL; +@@ -357,6 +365,13 @@ hash_netnet6_data_next(struct hash_netnet4_elem *next, + #define IP_SET_EMIT_CREATE + #include "ip_set_hash_gen.h" + ++static void ++hash_netnet6_init(struct hash_netnet6_elem *e) ++{ ++ e->cidr[0] = HOST_MASK; ++ e->cidr[1] = HOST_MASK; ++} ++ + static int + hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, +@@ -385,13 +400,14 @@ hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) + { + ipset_adtfn adtfn = set->variant->adt[adt]; +- struct hash_netnet6_elem e = { .cidr = { HOST_MASK, HOST_MASK, }, }; ++ struct hash_netnet6_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + int ret; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + ++ hash_netnet6_init(&e); + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) + return -IPSET_ERR_PROTOCOL; +diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c +index 0c68734f5cc4..9a14c237830f 100644 +--- a/net/netfilter/ipset/ip_set_hash_netportnet.c ++++ b/net/netfilter/ipset/ip_set_hash_netportnet.c +@@ -142,6 +142,13 @@ hash_netportnet4_data_next(struct hash_netportnet4_elem *next, + #define HOST_MASK 32 + #include "ip_set_hash_gen.h" + ++static void ++hash_netportnet4_init(struct hash_netportnet4_elem *e) ++{ ++ e->cidr[0] = HOST_MASK; ++ e->cidr[1] = HOST_MASK; ++} ++ + static int + hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, +@@ -175,7 +182,7 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], + { + const struct hash_netportnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; +- struct hash_netportnet4_elem e = { .cidr = { HOST_MASK, HOST_MASK, }, }; ++ struct hash_netportnet4_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to; + u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; +@@ -185,6 +192,7 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + ++ hash_netportnet4_init(&e); + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || +@@ -412,6 +420,13 @@ hash_netportnet6_data_next(struct hash_netportnet4_elem *next, + #define IP_SET_EMIT_CREATE + #include "ip_set_hash_gen.h" + ++static void ++hash_netportnet6_init(struct hash_netportnet6_elem *e) ++{ ++ e->cidr[0] = HOST_MASK; ++ e->cidr[1] = HOST_MASK; ++} ++ + static int + hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, +@@ -445,7 +460,7 @@ hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[], + { + const struct hash_netportnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; +- struct hash_netportnet6_elem e = { .cidr = { HOST_MASK, HOST_MASK, }, }; ++ struct hash_netportnet6_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 port, port_to; + bool with_ports = false; +@@ -454,6 +469,7 @@ hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[], + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + ++ hash_netportnet6_init(&e); + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || +diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c +index 3c20d02aee73..0625a42df108 100644 +--- a/net/netfilter/nf_conntrack_core.c ++++ b/net/netfilter/nf_conntrack_core.c +@@ -320,12 +320,13 @@ out_free: + } + EXPORT_SYMBOL_GPL(nf_ct_tmpl_alloc); + +-static void nf_ct_tmpl_free(struct nf_conn *tmpl) ++void nf_ct_tmpl_free(struct nf_conn *tmpl) + { + nf_ct_ext_destroy(tmpl); + nf_ct_ext_free(tmpl); + kfree(tmpl); + } ++EXPORT_SYMBOL_GPL(nf_ct_tmpl_free); + + static void + destroy_conntrack(struct nf_conntrack *nfct) +diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c +index 675d12c69e32..a5d41dfa9f05 100644 +--- a/net/netfilter/nf_log.c ++++ b/net/netfilter/nf_log.c +@@ -107,12 +107,17 @@ EXPORT_SYMBOL(nf_log_register); + + void nf_log_unregister(struct nf_logger *logger) + { ++ const struct nf_logger *log; + int i; + + mutex_lock(&nf_log_mutex); +- for (i = 0; i < NFPROTO_NUMPROTO; i++) +- RCU_INIT_POINTER(loggers[i][logger->type], NULL); ++ for (i = 0; i < NFPROTO_NUMPROTO; i++) { ++ log = nft_log_dereference(loggers[i][logger->type]); ++ if (log == logger) ++ RCU_INIT_POINTER(loggers[i][logger->type], NULL); ++ } + mutex_unlock(&nf_log_mutex); ++ synchronize_rcu(); + } + EXPORT_SYMBOL(nf_log_unregister); + +diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c +index d7f168527903..d6ee8f8b19b6 100644 +--- a/net/netfilter/nf_synproxy_core.c ++++ b/net/netfilter/nf_synproxy_core.c +@@ -378,7 +378,7 @@ static int __net_init synproxy_net_init(struct net *net) + err3: + free_percpu(snet->stats); + err2: +- nf_conntrack_free(ct); ++ nf_ct_tmpl_free(ct); + err1: + return err; + } +diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c +index 0c0e8ecf02ab..70277b11f742 100644 +--- a/net/netfilter/nfnetlink.c ++++ b/net/netfilter/nfnetlink.c +@@ -444,6 +444,7 @@ done: + static void nfnetlink_rcv(struct sk_buff *skb) + { + struct nlmsghdr *nlh = nlmsg_hdr(skb); ++ u_int16_t res_id; + int msglen; + + if (nlh->nlmsg_len < NLMSG_HDRLEN || +@@ -468,7 +469,12 @@ static void nfnetlink_rcv(struct sk_buff *skb) + + nfgenmsg = nlmsg_data(nlh); + skb_pull(skb, msglen); +- nfnetlink_rcv_batch(skb, nlh, nfgenmsg->res_id); ++ /* Work around old nft using host byte order */ ++ if (nfgenmsg->res_id == NFNL_SUBSYS_NFTABLES) ++ res_id = NFNL_SUBSYS_NFTABLES; ++ else ++ res_id = ntohs(nfgenmsg->res_id); ++ nfnetlink_rcv_batch(skb, nlh, res_id); + } else { + netlink_rcv_skb(skb, &nfnetlink_rcv_msg); + } +diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c +index 66def315eb56..9c8fab00164b 100644 +--- a/net/netfilter/nft_compat.c ++++ b/net/netfilter/nft_compat.c +@@ -619,6 +619,13 @@ struct nft_xt { + + static struct nft_expr_type nft_match_type; + ++static bool nft_match_cmp(const struct xt_match *match, ++ const char *name, u32 rev, u32 family) ++{ ++ return strcmp(match->name, name) == 0 && match->revision == rev && ++ (match->family == NFPROTO_UNSPEC || match->family == family); ++} ++ + static const struct nft_expr_ops * + nft_match_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +@@ -626,7 +633,7 @@ nft_match_select_ops(const struct nft_ctx *ctx, + struct nft_xt *nft_match; + struct xt_match *match; + char *mt_name; +- __u32 rev, family; ++ u32 rev, family; + + if (tb[NFTA_MATCH_NAME] == NULL || + tb[NFTA_MATCH_REV] == NULL || +@@ -641,8 +648,7 @@ nft_match_select_ops(const struct nft_ctx *ctx, + list_for_each_entry(nft_match, &nft_match_list, head) { + struct xt_match *match = nft_match->ops.data; + +- if (strcmp(match->name, mt_name) == 0 && +- match->revision == rev && match->family == family) { ++ if (nft_match_cmp(match, mt_name, rev, family)) { + if (!try_module_get(match->me)) + return ERR_PTR(-ENOENT); + +@@ -693,6 +699,13 @@ static LIST_HEAD(nft_target_list); + + static struct nft_expr_type nft_target_type; + ++static bool nft_target_cmp(const struct xt_target *tg, ++ const char *name, u32 rev, u32 family) ++{ ++ return strcmp(tg->name, name) == 0 && tg->revision == rev && ++ (tg->family == NFPROTO_UNSPEC || tg->family == family); ++} ++ + static const struct nft_expr_ops * + nft_target_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +@@ -700,7 +713,7 @@ nft_target_select_ops(const struct nft_ctx *ctx, + struct nft_xt *nft_target; + struct xt_target *target; + char *tg_name; +- __u32 rev, family; ++ u32 rev, family; + + if (tb[NFTA_TARGET_NAME] == NULL || + tb[NFTA_TARGET_REV] == NULL || +@@ -715,8 +728,7 @@ nft_target_select_ops(const struct nft_ctx *ctx, + list_for_each_entry(nft_target, &nft_target_list, head) { + struct xt_target *target = nft_target->ops.data; + +- if (strcmp(target->name, tg_name) == 0 && +- target->revision == rev && target->family == family) { ++ if (nft_target_cmp(target, tg_name, rev, family)) { + if (!try_module_get(target->me)) + return ERR_PTR(-ENOENT); + +diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c +index 43ddeee404e9..f3377ce1ff18 100644 +--- a/net/netfilter/xt_CT.c ++++ b/net/netfilter/xt_CT.c +@@ -233,7 +233,7 @@ out: + return 0; + + err3: +- nf_conntrack_free(ct); ++ nf_ct_tmpl_free(ct); + err2: + nf_ct_l3proto_module_put(par->family); + err1: +diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c +index d25cd430f9ff..95412abc95b0 100644 +--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c +@@ -384,6 +384,7 @@ static int send_reply(struct svcxprt_rdma *rdma, + int byte_count) + { + struct ib_send_wr send_wr; ++ u32 xdr_off; + int sge_no; + int sge_bytes; + int page_no; +@@ -418,8 +419,8 @@ static int send_reply(struct svcxprt_rdma *rdma, + ctxt->direction = DMA_TO_DEVICE; + + /* Map the payload indicated by 'byte_count' */ ++ xdr_off = 0; + for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) { +- int xdr_off = 0; + sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count); + byte_count -= sge_bytes; + ctxt->sge[sge_no].addr = +@@ -457,6 +458,13 @@ static int send_reply(struct svcxprt_rdma *rdma, + } + rqstp->rq_next_page = rqstp->rq_respages + 1; + ++ /* The loop above bumps sc_dma_used for each sge. The ++ * xdr_buf.tail gets a separate sge, but resides in the ++ * same page as xdr_buf.head. Don't count it twice. ++ */ ++ if (sge_no > ctxt->count) ++ atomic_dec(&rdma->sc_dma_used); ++ + if (sge_no > rdma->sc_max_sge) { + pr_err("svcrdma: Too many sges (%d)\n", sge_no); + goto err; +diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig +index 885683a3b0bd..e0406211716b 100644 +--- a/sound/arm/Kconfig ++++ b/sound/arm/Kconfig +@@ -9,6 +9,14 @@ menuconfig SND_ARM + Drivers that are implemented on ASoC can be found in + "ALSA for SoC audio support" section. + ++config SND_PXA2XX_LIB ++ tristate ++ select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 ++ select SND_DMAENGINE_PCM ++ ++config SND_PXA2XX_LIB_AC97 ++ bool ++ + if SND_ARM + + config SND_ARMAACI +@@ -21,13 +29,6 @@ config SND_PXA2XX_PCM + tristate + select SND_PCM + +-config SND_PXA2XX_LIB +- tristate +- select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 +- +-config SND_PXA2XX_LIB_AC97 +- bool +- + config SND_PXA2XX_AC97 + tristate "AC97 driver for the Intel PXA2xx chip" + depends on ARCH_PXA +diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c +index 477742cb70a2..58c0aad37284 100644 +--- a/sound/pci/hda/hda_tegra.c ++++ b/sound/pci/hda/hda_tegra.c +@@ -73,6 +73,7 @@ struct hda_tegra { + struct clk *hda2codec_2x_clk; + struct clk *hda2hdmi_clk; + void __iomem *regs; ++ struct work_struct probe_work; + }; + + #ifdef CONFIG_PM +@@ -294,7 +295,9 @@ static int hda_tegra_dev_disconnect(struct snd_device *device) + static int hda_tegra_dev_free(struct snd_device *device) + { + struct azx *chip = device->device_data; ++ struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + ++ cancel_work_sync(&hda->probe_work); + if (azx_bus(chip)->chip_init) { + azx_stop_all_streams(chip); + azx_stop_chip(chip); +@@ -426,6 +429,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) + /* + * constructor + */ ++ ++static void hda_tegra_probe_work(struct work_struct *work); ++ + static int hda_tegra_create(struct snd_card *card, + unsigned int driver_caps, + struct hda_tegra *hda) +@@ -452,6 +458,8 @@ static int hda_tegra_create(struct snd_card *card, + chip->single_cmd = false; + chip->snoop = true; + ++ INIT_WORK(&hda->probe_work, hda_tegra_probe_work); ++ + err = azx_bus_init(chip, NULL, &hda_tegra_io_ops); + if (err < 0) + return err; +@@ -499,6 +507,21 @@ static int hda_tegra_probe(struct platform_device *pdev) + card->private_data = chip; + + dev_set_drvdata(&pdev->dev, card); ++ schedule_work(&hda->probe_work); ++ ++ return 0; ++ ++out_free: ++ snd_card_free(card); ++ return err; ++} ++ ++static void hda_tegra_probe_work(struct work_struct *work) ++{ ++ struct hda_tegra *hda = container_of(work, struct hda_tegra, probe_work); ++ struct azx *chip = &hda->chip; ++ struct platform_device *pdev = to_platform_device(hda->dev); ++ int err; + + err = hda_tegra_first_init(chip, pdev); + if (err < 0) +@@ -520,11 +543,8 @@ static int hda_tegra_probe(struct platform_device *pdev) + chip->running = 1; + snd_hda_set_power_save(&chip->bus, power_save * 1000); + +- return 0; +- +-out_free: +- snd_card_free(card); +- return err; ++ out_free: ++ return; /* no error return from async probe */ + } + + static int hda_tegra_remove(struct platform_device *pdev) +diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c +index 584a0343ab0c..85813de26da8 100644 +--- a/sound/pci/hda/patch_cirrus.c ++++ b/sound/pci/hda/patch_cirrus.c +@@ -633,6 +633,7 @@ static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = { + SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11), + SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6), + SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6), ++ SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11), + {} /* terminator */ + }; + +diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c +index c8f01ccc2513..6a66139871c6 100644 +--- a/sound/pci/hda/patch_realtek.c ++++ b/sound/pci/hda/patch_realtek.c +@@ -4188,6 +4188,24 @@ static void alc_fixup_disable_aamix(struct hda_codec *codec, + } + } + ++/* fixup for Thinkpad docks: add dock pins, avoid HP parser fixup */ ++static void alc_fixup_tpt440_dock(struct hda_codec *codec, ++ const struct hda_fixup *fix, int action) ++{ ++ static const struct hda_pintbl pincfgs[] = { ++ { 0x16, 0x21211010 }, /* dock headphone */ ++ { 0x19, 0x21a11010 }, /* dock mic */ ++ { } ++ }; ++ struct alc_spec *spec = codec->spec; ++ ++ if (action == HDA_FIXUP_ACT_PRE_PROBE) { ++ spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; ++ codec->power_save_node = 0; /* avoid click noises */ ++ snd_hda_apply_pincfgs(codec, pincfgs); ++ } ++} ++ + static void alc_shutup_dell_xps13(struct hda_codec *codec) + { + struct alc_spec *spec = codec->spec; +@@ -4562,7 +4580,6 @@ enum { + ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC, + ALC293_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC292_FIXUP_TPT440_DOCK, +- ALC292_FIXUP_TPT440_DOCK2, + ALC283_FIXUP_BXBT2807_MIC, + ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED, + ALC282_FIXUP_ASPIRE_V5_PINS, +@@ -5029,17 +5046,7 @@ static const struct hda_fixup alc269_fixups[] = { + }, + [ALC292_FIXUP_TPT440_DOCK] = { + .type = HDA_FIXUP_FUNC, +- .v.func = alc269_fixup_pincfg_no_hp_to_lineout, +- .chained = true, +- .chain_id = ALC292_FIXUP_TPT440_DOCK2 +- }, +- [ALC292_FIXUP_TPT440_DOCK2] = { +- .type = HDA_FIXUP_PINS, +- .v.pins = (const struct hda_pintbl[]) { +- { 0x16, 0x21211010 }, /* dock headphone */ +- { 0x19, 0x21a11010 }, /* dock mic */ +- { } +- }, ++ .v.func = alc_fixup_tpt440_dock, + .chained = true, + .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST + }, +@@ -5299,6 +5306,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { + SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), ++ SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), + SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), +diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c +index 9d947aef2c8b..def5cc8dff02 100644 +--- a/sound/pci/hda/patch_sigmatel.c ++++ b/sound/pci/hda/patch_sigmatel.c +@@ -4520,7 +4520,11 @@ static int patch_stac92hd73xx(struct hda_codec *codec) + return err; + + spec = codec->spec; +- codec->power_save_node = 1; ++ /* enable power_save_node only for new 92HD89xx chips, as it causes ++ * click noises on old 92HD73xx chips. ++ */ ++ if ((codec->core.vendor_id & 0xfffffff0) != 0x111d7670) ++ codec->power_save_node = 1; + spec->linear_tone_beep = 0; + spec->gen.mixer_nid = 0x1d; + spec->have_spdif_mux = 1; +diff --git a/sound/soc/au1x/db1200.c b/sound/soc/au1x/db1200.c +index 58c3164802b8..8c907ebea189 100644 +--- a/sound/soc/au1x/db1200.c ++++ b/sound/soc/au1x/db1200.c +@@ -129,6 +129,8 @@ static struct snd_soc_dai_link db1300_i2s_dai = { + .cpu_dai_name = "au1xpsc_i2s.2", + .platform_name = "au1xpsc-pcm.2", + .codec_name = "wm8731.0-001b", ++ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBM_CFM, + .ops = &db1200_i2s_wm8731_ops, + }; + +@@ -146,6 +148,8 @@ static struct snd_soc_dai_link db1550_i2s_dai = { + .cpu_dai_name = "au1xpsc_i2s.3", + .platform_name = "au1xpsc-pcm.3", + .codec_name = "wm8731.0-001b", ++ .dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBM_CFM, + .ops = &db1200_i2s_wm8731_ops, + }; + +diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c +index e673f6ceb521..7c411297bfdd 100644 +--- a/sound/soc/codecs/sgtl5000.c ++++ b/sound/soc/codecs/sgtl5000.c +@@ -1377,8 +1377,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec) + sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT); + + snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL, +- SGTL5000_BIAS_R_MASK, +- sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT); ++ SGTL5000_BIAS_VOLT_MASK, ++ sgtl5000->micbias_voltage << SGTL5000_BIAS_VOLT_SHIFT); + /* + * disable DAP + * TODO: +diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c +index 4f25a7d0efa2..b3e5685aca1e 100644 +--- a/sound/soc/codecs/tas2552.c ++++ b/sound/soc/codecs/tas2552.c +@@ -551,7 +551,7 @@ static struct snd_soc_dai_driver tas2552_dai[] = { + /* + * DAC digital volumes. From -7 to 24 dB in 1 dB steps + */ +-static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 0); ++static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0); + + static const char * const tas2552_din_source_select[] = { + "Muted", +diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c +index a3e97b46b64e..0d28e3b356f6 100644 +--- a/sound/soc/dwc/designware_i2s.c ++++ b/sound/soc/dwc/designware_i2s.c +@@ -131,10 +131,10 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + for (i = 0; i < 4; i++) +- i2s_write_reg(dev->i2s_base, TOR(i), 0); ++ i2s_read_reg(dev->i2s_base, TOR(i)); + } else { + for (i = 0; i < 4; i++) +- i2s_write_reg(dev->i2s_base, ROR(i), 0); ++ i2s_read_reg(dev->i2s_base, ROR(i)); + } + } + +diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig +index 39cea80846c3..f2bf8661dd21 100644 +--- a/sound/soc/pxa/Kconfig ++++ b/sound/soc/pxa/Kconfig +@@ -1,7 +1,6 @@ + config SND_PXA2XX_SOC + tristate "SoC Audio for the Intel PXA2xx chip" + depends on ARCH_PXA +- select SND_ARM + select SND_PXA2XX_LIB + help + Say Y or M if you want to add support for codecs attached to +@@ -25,7 +24,6 @@ config SND_PXA2XX_AC97 + config SND_PXA2XX_SOC_AC97 + tristate + select AC97_BUS +- select SND_ARM + select SND_PXA2XX_LIB_AC97 + select SND_SOC_AC97_BUS + +diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c +index 1f6054650991..9e4b04e0fbd1 100644 +--- a/sound/soc/pxa/pxa2xx-ac97.c ++++ b/sound/soc/pxa/pxa2xx-ac97.c +@@ -49,7 +49,7 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { + .reset = pxa2xx_ac97_cold_reset, + }; + +-static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12; ++static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, +@@ -57,7 +57,7 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = { + .filter_data = &pxa2xx_ac97_pcm_stereo_in_req, + }; + +-static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11; ++static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12; + static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = { + .addr = __PREG(PCDR), + .addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, +diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c +index 82e350e9501c..ac75816ada7c 100644 +--- a/sound/synth/emux/emux_oss.c ++++ b/sound/synth/emux/emux_oss.c +@@ -69,7 +69,8 @@ snd_emux_init_seq_oss(struct snd_emux *emu) + struct snd_seq_oss_reg *arg; + struct snd_seq_device *dev; + +- if (snd_seq_device_new(emu->card, 0, SNDRV_SEQ_DEV_ID_OSS, ++ /* using device#1 here for avoiding conflicts with OPL3 */ ++ if (snd_seq_device_new(emu->card, 1, SNDRV_SEQ_DEV_ID_OSS, + sizeof(struct snd_seq_oss_reg), &dev) < 0) + return; + +diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c +index e44052483ed9..80159e6811c2 100644 +--- a/tools/lguest/lguest.c ++++ b/tools/lguest/lguest.c +@@ -125,7 +125,11 @@ struct device_list { + /* The list of Guest devices, based on command line arguments. */ + static struct device_list devices; + +-struct virtio_pci_cfg_cap { ++/* ++ * Just like struct virtio_pci_cfg_cap in uapi/linux/virtio_pci.h, ++ * but uses a u32 explicitly for the data. ++ */ ++struct virtio_pci_cfg_cap_u32 { + struct virtio_pci_cap cap; + u32 pci_cfg_data; /* Data for BAR access. */ + }; +@@ -157,7 +161,7 @@ struct pci_config { + struct virtio_pci_notify_cap notify; + struct virtio_pci_cap isr; + struct virtio_pci_cap device; +- struct virtio_pci_cfg_cap cfg_access; ++ struct virtio_pci_cfg_cap_u32 cfg_access; + }; + + /* The device structure describes a single device. */ +@@ -1291,7 +1295,7 @@ static struct device *dev_and_reg(u32 *reg) + * only fault if they try to write with some invalid bar/offset/length. + */ + static bool valid_bar_access(struct device *d, +- struct virtio_pci_cfg_cap *cfg_access) ++ struct virtio_pci_cfg_cap_u32 *cfg_access) + { + /* We only have 1 bar (BAR0) */ + if (cfg_access->cap.bar != 0) +diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c +index cc25f059ab3d..a843bee66a4f 100644 +--- a/tools/lib/traceevent/event-parse.c ++++ b/tools/lib/traceevent/event-parse.c +@@ -3721,7 +3721,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, + struct format_field *field; + struct printk_map *printk; + long long val, fval; +- unsigned long addr; ++ unsigned long long addr; + char *str; + unsigned char *hex; + int print; +@@ -3754,13 +3754,30 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, + */ + if (!(field->flags & FIELD_IS_ARRAY) && + field->size == pevent->long_size) { +- addr = *(unsigned long *)(data + field->offset); ++ ++ /* Handle heterogeneous recording and processing ++ * architectures ++ * ++ * CASE I: ++ * Traces recorded on 32-bit devices (32-bit ++ * addressing) and processed on 64-bit devices: ++ * In this case, only 32 bits should be read. ++ * ++ * CASE II: ++ * Traces recorded on 64 bit devices and processed ++ * on 32-bit devices: ++ * In this case, 64 bits must be read. ++ */ ++ addr = (pevent->long_size == 8) ? ++ *(unsigned long long *)(data + field->offset) : ++ (unsigned long long)*(unsigned int *)(data + field->offset); ++ + /* Check if it matches a print format */ + printk = find_printk(pevent, addr); + if (printk) + trace_seq_puts(s, printk->printk); + else +- trace_seq_printf(s, "%lx", addr); ++ trace_seq_printf(s, "%llx", addr); + break; + } + str = malloc(len + 1); +diff --git a/tools/perf/arch/alpha/Build b/tools/perf/arch/alpha/Build +new file mode 100644 +index 000000000000..1bb8bf6d7fd4 +--- /dev/null ++++ b/tools/perf/arch/alpha/Build +@@ -0,0 +1 @@ ++# empty +diff --git a/tools/perf/arch/mips/Build b/tools/perf/arch/mips/Build +new file mode 100644 +index 000000000000..1bb8bf6d7fd4 +--- /dev/null ++++ b/tools/perf/arch/mips/Build +@@ -0,0 +1 @@ ++# empty +diff --git a/tools/perf/arch/parisc/Build b/tools/perf/arch/parisc/Build +new file mode 100644 +index 000000000000..1bb8bf6d7fd4 +--- /dev/null ++++ b/tools/perf/arch/parisc/Build +@@ -0,0 +1 @@ ++# empty +diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c +index d99d850e1444..ef355fc0e870 100644 +--- a/tools/perf/builtin-stat.c ++++ b/tools/perf/builtin-stat.c +@@ -694,7 +694,7 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) + static void print_aggr(char *prefix) + { + struct perf_evsel *counter; +- int cpu, cpu2, s, s2, id, nr; ++ int cpu, s, s2, id, nr; + double uval; + u64 ena, run, val; + +@@ -707,8 +707,7 @@ static void print_aggr(char *prefix) + val = ena = run = 0; + nr = 0; + for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { +- cpu2 = perf_evsel__cpus(counter)->map[cpu]; +- s2 = aggr_get_id(evsel_list->cpus, cpu2); ++ s2 = aggr_get_id(perf_evsel__cpus(counter), cpu); + if (s2 != id) + continue; + val += perf_counts(counter->counts, cpu, 0)->val; +diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c +index 03ace57a800c..4215cc155041 100644 +--- a/tools/perf/util/header.c ++++ b/tools/perf/util/header.c +@@ -1442,7 +1442,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, + if (ph->needs_swap) + nr = bswap_32(nr); + +- ph->env.nr_cpus_online = nr; ++ ph->env.nr_cpus_avail = nr; + + ret = readn(fd, &nr, sizeof(nr)); + if (ret != sizeof(nr)) +@@ -1451,7 +1451,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, + if (ph->needs_swap) + nr = bswap_32(nr); + +- ph->env.nr_cpus_avail = nr; ++ ph->env.nr_cpus_online = nr; + return 0; + } + +diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c +index 6f28d53d4e46..f298c696e24f 100644 +--- a/tools/perf/util/hist.c ++++ b/tools/perf/util/hist.c +@@ -151,6 +151,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) + hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); + hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); + ++ if (h->srcline) ++ hists__new_col_len(hists, HISTC_SRCLINE, strlen(h->srcline)); ++ + if (h->transaction) + hists__new_col_len(hists, HISTC_TRANSACTION, + hist_entry__transaction_len()); +diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y +index 591905a02b92..9cd70819c795 100644 +--- a/tools/perf/util/parse-events.y ++++ b/tools/perf/util/parse-events.y +@@ -255,7 +255,7 @@ PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc + list_add_tail(&term->list, head); + + ALLOC_LIST(list); +- ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); ++ ABORT_ON(parse_events_add_pmu(data, list, "cpu", head)); + parse_events__free_terms(head); + $$ = list; + } +diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c +index 381f23a443c7..ae6351db6de4 100644 +--- a/tools/perf/util/probe-event.c ++++ b/tools/perf/util/probe-event.c +@@ -274,12 +274,13 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso) + int ret = 0; + + if (module) { +- list_for_each_entry(dso, &host_machine->dsos.head, node) { +- if (!dso->kernel) +- continue; +- if (strncmp(dso->short_name + 1, module, +- dso->short_name_len - 2) == 0) +- goto found; ++ char module_name[128]; ++ ++ snprintf(module_name, sizeof(module_name), "[%s]", module); ++ map = map_groups__find_by_name(&host_machine->kmaps, MAP__FUNCTION, module_name); ++ if (map) { ++ dso = map->dso; ++ goto found; + } + pr_debug("Failed to find module %s.\n", module); + return -ENOENT; +diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h +index 31db6ee7db54..cd55c6db421d 100644 +--- a/tools/perf/util/probe-event.h ++++ b/tools/perf/util/probe-event.h +@@ -106,6 +106,8 @@ struct variable_list { + struct strlist *vars; /* Available variables */ + }; + ++struct map; ++ + /* Command string to events */ + extern int parse_perf_probe_command(const char *cmd, + struct perf_probe_event *pev); +diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c +index 65f7e389ae09..333858821ab0 100644 +--- a/tools/perf/util/symbol-elf.c ++++ b/tools/perf/util/symbol-elf.c +@@ -1260,8 +1260,6 @@ out_close: + static int kcore__init(struct kcore *kcore, char *filename, int elfclass, + bool temp) + { +- GElf_Ehdr *ehdr; +- + kcore->elfclass = elfclass; + + if (temp) +@@ -1278,9 +1276,7 @@ static int kcore__init(struct kcore *kcore, char *filename, int elfclass, + if (!gelf_newehdr(kcore->elf, elfclass)) + goto out_end; + +- ehdr = gelf_getehdr(kcore->elf, &kcore->ehdr); +- if (!ehdr) +- goto out_end; ++ memset(&kcore->ehdr, 0, sizeof(GElf_Ehdr)); + + return 0; + +@@ -1337,23 +1333,18 @@ static int kcore__copy_hdr(struct kcore *from, struct kcore *to, size_t count) + static int kcore__add_phdr(struct kcore *kcore, int idx, off_t offset, + u64 addr, u64 len) + { +- GElf_Phdr gphdr; +- GElf_Phdr *phdr; +- +- phdr = gelf_getphdr(kcore->elf, idx, &gphdr); +- if (!phdr) +- return -1; +- +- phdr->p_type = PT_LOAD; +- phdr->p_flags = PF_R | PF_W | PF_X; +- phdr->p_offset = offset; +- phdr->p_vaddr = addr; +- phdr->p_paddr = 0; +- phdr->p_filesz = len; +- phdr->p_memsz = len; +- phdr->p_align = page_size; +- +- if (!gelf_update_phdr(kcore->elf, idx, phdr)) ++ GElf_Phdr phdr = { ++ .p_type = PT_LOAD, ++ .p_flags = PF_R | PF_W | PF_X, ++ .p_offset = offset, ++ .p_vaddr = addr, ++ .p_paddr = 0, ++ .p_filesz = len, ++ .p_memsz = len, ++ .p_align = page_size, ++ }; ++ ++ if (!gelf_update_phdr(kcore->elf, idx, &phdr)) + return -1; + + return 0; +diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c +index 9ff4193dfa49..79db45336e3a 100644 +--- a/virt/kvm/eventfd.c ++++ b/virt/kvm/eventfd.c +@@ -771,40 +771,14 @@ static enum kvm_bus ioeventfd_bus_from_flags(__u32 flags) + return KVM_MMIO_BUS; + } + +-static int +-kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) ++static int kvm_assign_ioeventfd_idx(struct kvm *kvm, ++ enum kvm_bus bus_idx, ++ struct kvm_ioeventfd *args) + { +- enum kvm_bus bus_idx; +- struct _ioeventfd *p; +- struct eventfd_ctx *eventfd; +- int ret; +- +- bus_idx = ioeventfd_bus_from_flags(args->flags); +- /* must be natural-word sized, or 0 to ignore length */ +- switch (args->len) { +- case 0: +- case 1: +- case 2: +- case 4: +- case 8: +- break; +- default: +- return -EINVAL; +- } +- +- /* check for range overflow */ +- if (args->addr + args->len < args->addr) +- return -EINVAL; + +- /* check for extra flags that we don't understand */ +- if (args->flags & ~KVM_IOEVENTFD_VALID_FLAG_MASK) +- return -EINVAL; +- +- /* ioeventfd with no length can't be combined with DATAMATCH */ +- if (!args->len && +- args->flags & (KVM_IOEVENTFD_FLAG_PIO | +- KVM_IOEVENTFD_FLAG_DATAMATCH)) +- return -EINVAL; ++ struct eventfd_ctx *eventfd; ++ struct _ioeventfd *p; ++ int ret; + + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) +@@ -843,16 +817,6 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) + if (ret < 0) + goto unlock_fail; + +- /* When length is ignored, MMIO is also put on a separate bus, for +- * faster lookups. +- */ +- if (!args->len && !(args->flags & KVM_IOEVENTFD_FLAG_PIO)) { +- ret = kvm_io_bus_register_dev(kvm, KVM_FAST_MMIO_BUS, +- p->addr, 0, &p->dev); +- if (ret < 0) +- goto register_fail; +- } +- + kvm->buses[bus_idx]->ioeventfd_count++; + list_add_tail(&p->list, &kvm->ioeventfds); + +@@ -860,8 +824,6 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) + + return 0; + +-register_fail: +- kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev); + unlock_fail: + mutex_unlock(&kvm->slots_lock); + +@@ -873,14 +835,13 @@ fail: + } + + static int +-kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) ++kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx, ++ struct kvm_ioeventfd *args) + { +- enum kvm_bus bus_idx; + struct _ioeventfd *p, *tmp; + struct eventfd_ctx *eventfd; + int ret = -ENOENT; + +- bus_idx = ioeventfd_bus_from_flags(args->flags); + eventfd = eventfd_ctx_fdget(args->fd); + if (IS_ERR(eventfd)) + return PTR_ERR(eventfd); +@@ -901,10 +862,6 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) + continue; + + kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev); +- if (!p->length) { +- kvm_io_bus_unregister_dev(kvm, KVM_FAST_MMIO_BUS, +- &p->dev); +- } + kvm->buses[bus_idx]->ioeventfd_count--; + ioeventfd_release(p); + ret = 0; +@@ -918,6 +875,71 @@ kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) + return ret; + } + ++static int kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) ++{ ++ enum kvm_bus bus_idx = ioeventfd_bus_from_flags(args->flags); ++ int ret = kvm_deassign_ioeventfd_idx(kvm, bus_idx, args); ++ ++ if (!args->len && bus_idx == KVM_MMIO_BUS) ++ kvm_deassign_ioeventfd_idx(kvm, KVM_FAST_MMIO_BUS, args); ++ ++ return ret; ++} ++ ++static int ++kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) ++{ ++ enum kvm_bus bus_idx; ++ int ret; ++ ++ bus_idx = ioeventfd_bus_from_flags(args->flags); ++ /* must be natural-word sized, or 0 to ignore length */ ++ switch (args->len) { ++ case 0: ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* check for range overflow */ ++ if (args->addr + args->len < args->addr) ++ return -EINVAL; ++ ++ /* check for extra flags that we don't understand */ ++ if (args->flags & ~KVM_IOEVENTFD_VALID_FLAG_MASK) ++ return -EINVAL; ++ ++ /* ioeventfd with no length can't be combined with DATAMATCH */ ++ if (!args->len && ++ args->flags & (KVM_IOEVENTFD_FLAG_PIO | ++ KVM_IOEVENTFD_FLAG_DATAMATCH)) ++ return -EINVAL; ++ ++ ret = kvm_assign_ioeventfd_idx(kvm, bus_idx, args); ++ if (ret) ++ goto fail; ++ ++ /* When length is ignored, MMIO is also put on a separate bus, for ++ * faster lookups. ++ */ ++ if (!args->len && bus_idx == KVM_MMIO_BUS) { ++ ret = kvm_assign_ioeventfd_idx(kvm, KVM_FAST_MMIO_BUS, args); ++ if (ret < 0) ++ goto fast_fail; ++ } ++ ++ return 0; ++ ++fast_fail: ++ kvm_deassign_ioeventfd_idx(kvm, bus_idx, args); ++fail: ++ return ret; ++} ++ + int + kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) + { +diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c +index 8b8a44453670..5a2a78a91d58 100644 +--- a/virt/kvm/kvm_main.c ++++ b/virt/kvm/kvm_main.c +@@ -3080,10 +3080,25 @@ static void kvm_io_bus_destroy(struct kvm_io_bus *bus) + static inline int kvm_io_bus_cmp(const struct kvm_io_range *r1, + const struct kvm_io_range *r2) + { +- if (r1->addr < r2->addr) ++ gpa_t addr1 = r1->addr; ++ gpa_t addr2 = r2->addr; ++ ++ if (addr1 < addr2) + return -1; +- if (r1->addr + r1->len > r2->addr + r2->len) ++ ++ /* If r2->len == 0, match the exact address. If r2->len != 0, ++ * accept any overlapping write. Any order is acceptable for ++ * overlapping ranges, because kvm_io_bus_get_first_dev ensures ++ * we process all of them. ++ */ ++ if (r2->len) { ++ addr1 += r1->len; ++ addr2 += r2->len; ++ } ++ ++ if (addr1 > addr2) + return 1; ++ + return 0; + } + diff --git a/patch/kernel/odroid-next/patch-4.2.4-5.patch b/patch/kernel/odroid-next/patch-4.2.4-5.patch new file mode 100644 index 000000000..b866faf1f --- /dev/null +++ b/patch/kernel/odroid-next/patch-4.2.4-5.patch @@ -0,0 +1,1945 @@ +diff --git a/Makefile b/Makefile +index a952801a6cd5..96076dcad18e 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 2 +-SUBLEVEL = 4 ++SUBLEVEL = 5 + EXTRAVERSION = + NAME = Hurr durr I'ma sheep + +diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile +index 4418a5078833..c8643ac5db71 100644 +--- a/arch/arm/mach-ux500/Makefile ++++ b/arch/arm/mach-ux500/Makefile +@@ -7,7 +7,7 @@ obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o + obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o + obj-$(CONFIG_MACH_MOP500) += board-mop500-regulators.o \ + board-mop500-audio.o +-obj-$(CONFIG_SMP) += platsmp.o headsmp.o ++obj-$(CONFIG_SMP) += platsmp.o + obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o + obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o + +diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c +index 16913800bbf9..ba708ce08616 100644 +--- a/arch/arm/mach-ux500/cpu-db8500.c ++++ b/arch/arm/mach-ux500/cpu-db8500.c +@@ -154,7 +154,6 @@ static const char * stericsson_dt_platform_compat[] = { + }; + + DT_MACHINE_START(U8500_DT, "ST-Ericsson Ux5x0 platform (Device Tree Support)") +- .smp = smp_ops(ux500_smp_ops), + .map_io = u8500_map_io, + .init_irq = ux500_init_irq, + /* we re-use nomadik timer here */ +diff --git a/arch/arm/mach-ux500/headsmp.S b/arch/arm/mach-ux500/headsmp.S +deleted file mode 100644 +index 9cdea049485d..000000000000 +--- a/arch/arm/mach-ux500/headsmp.S ++++ /dev/null +@@ -1,37 +0,0 @@ +-/* +- * Copyright (c) 2009 ST-Ericsson +- * This file is based ARM Realview platform +- * Copyright (c) 2003 ARM Limited +- * All Rights Reserved +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License version 2 as +- * published by the Free Software Foundation. +- */ +-#include +-#include +- +-/* +- * U8500 specific entry point for secondary CPUs. +- */ +-ENTRY(u8500_secondary_startup) +- mrc p15, 0, r0, c0, c0, 5 +- and r0, r0, #15 +- adr r4, 1f +- ldmia r4, {r5, r6} +- sub r4, r4, r5 +- add r6, r6, r4 +-pen: ldr r7, [r6] +- cmp r7, r0 +- bne pen +- +- /* +- * we've been released from the holding pen: secondary_stack +- * should now contain the SVC stack for this core +- */ +- b secondary_startup +-ENDPROC(u8500_secondary_startup) +- +- .align 2 +-1: .long . +- .long pen_release +diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c +index 62b1de922bd8..70766b963758 100644 +--- a/arch/arm/mach-ux500/platsmp.c ++++ b/arch/arm/mach-ux500/platsmp.c +@@ -28,135 +28,81 @@ + #include "db8500-regs.h" + #include "id.h" + +-static void __iomem *scu_base; +-static void __iomem *backupram; +- +-/* This is called from headsmp.S to wakeup the secondary core */ +-extern void u8500_secondary_startup(void); +- +-/* +- * Write pen_release in a way that is guaranteed to be visible to all +- * observers, irrespective of whether they're taking part in coherency +- * or not. This is necessary for the hotplug code to work reliably. +- */ +-static void write_pen_release(int val) +-{ +- pen_release = val; +- smp_wmb(); +- sync_cache_w(&pen_release); +-} +- +-static DEFINE_SPINLOCK(boot_lock); +- +-static void ux500_secondary_init(unsigned int cpu) +-{ +- /* +- * let the primary processor know we're out of the +- * pen, then head off into the C entry point +- */ +- write_pen_release(-1); +- +- /* +- * Synchronise with the boot thread. +- */ +- spin_lock(&boot_lock); +- spin_unlock(&boot_lock); +-} ++/* Magic triggers in backup RAM */ ++#define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4 ++#define UX500_CPU1_WAKEMAGIC_OFFSET 0x1FF0 + +-static int ux500_boot_secondary(unsigned int cpu, struct task_struct *idle) ++static void wakeup_secondary(void) + { +- unsigned long timeout; +- +- /* +- * set synchronisation state between this boot processor +- * and the secondary one +- */ +- spin_lock(&boot_lock); +- +- /* +- * The secondary processor is waiting to be released from +- * the holding pen - release it, then wait for it to flag +- * that it has been released by resetting pen_release. +- */ +- write_pen_release(cpu_logical_map(cpu)); +- +- arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ struct device_node *np; ++ static void __iomem *backupram; + +- timeout = jiffies + (1 * HZ); +- while (time_before(jiffies, timeout)) { +- if (pen_release == -1) +- break; ++ np = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram"); ++ if (!np) { ++ pr_err("No backupram base address\n"); ++ return; ++ } ++ backupram = of_iomap(np, 0); ++ of_node_put(np); ++ if (!backupram) { ++ pr_err("No backupram remap\n"); ++ return; + } + + /* +- * now the secondary core is starting up let it run its +- * calibrations, then wait for it to finish +- */ +- spin_unlock(&boot_lock); +- +- return pen_release != -1 ? -ENOSYS : 0; +-} +- +-static void __init wakeup_secondary(void) +-{ +- /* + * write the address of secondary startup into the backup ram register + * at offset 0x1FF4, then write the magic number 0xA1FEED01 to the + * backup ram register at offset 0x1FF0, which is what boot rom code +- * is waiting for. This would wake up the secondary core from WFE ++ * is waiting for. This will wake up the secondary core from WFE. + */ +-#define UX500_CPU1_JUMPADDR_OFFSET 0x1FF4 +- __raw_writel(virt_to_phys(u8500_secondary_startup), +- backupram + UX500_CPU1_JUMPADDR_OFFSET); +- +-#define UX500_CPU1_WAKEMAGIC_OFFSET 0x1FF0 +- __raw_writel(0xA1FEED01, +- backupram + UX500_CPU1_WAKEMAGIC_OFFSET); ++ writel(virt_to_phys(secondary_startup), ++ backupram + UX500_CPU1_JUMPADDR_OFFSET); ++ writel(0xA1FEED01, ++ backupram + UX500_CPU1_WAKEMAGIC_OFFSET); + + /* make sure write buffer is drained */ + mb(); ++ iounmap(backupram); + } + +-/* +- * Initialise the CPU possible map early - this describes the CPUs +- * which may be present or become present in the system. +- */ +-static void __init ux500_smp_init_cpus(void) ++static void __init ux500_smp_prepare_cpus(unsigned int max_cpus) + { +- unsigned int i, ncores; + struct device_node *np; ++ static void __iomem *scu_base; ++ unsigned int ncores; ++ int i; + + np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); ++ if (!np) { ++ pr_err("No SCU base address\n"); ++ return; ++ } + scu_base = of_iomap(np, 0); + of_node_put(np); +- if (!scu_base) ++ if (!scu_base) { ++ pr_err("No SCU remap\n"); + return; +- backupram = ioremap(U8500_BACKUPRAM0_BASE, SZ_8K); +- ncores = scu_get_core_count(scu_base); +- +- /* sanity check */ +- if (ncores > nr_cpu_ids) { +- pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", +- ncores, nr_cpu_ids); +- ncores = nr_cpu_ids; + } + ++ scu_enable(scu_base); ++ ncores = scu_get_core_count(scu_base); + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); ++ iounmap(scu_base); + } + +-static void __init ux500_smp_prepare_cpus(unsigned int max_cpus) ++static int ux500_boot_secondary(unsigned int cpu, struct task_struct *idle) + { +- scu_enable(scu_base); + wakeup_secondary(); ++ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); ++ return 0; + } + + struct smp_operations ux500_smp_ops __initdata = { +- .smp_init_cpus = ux500_smp_init_cpus, + .smp_prepare_cpus = ux500_smp_prepare_cpus, +- .smp_secondary_init = ux500_secondary_init, + .smp_boot_secondary = ux500_boot_secondary, + #ifdef CONFIG_HOTPLUG_CPU + .cpu_die = ux500_cpu_die, + #endif + }; ++CPU_METHOD_OF_DECLARE(ux500_smp, "ste,dbx500-smp", &ux500_smp_ops); +diff --git a/arch/arm/mach-ux500/setup.h b/arch/arm/mach-ux500/setup.h +index 1fb6ad2789f1..65876eac0761 100644 +--- a/arch/arm/mach-ux500/setup.h ++++ b/arch/arm/mach-ux500/setup.h +@@ -26,7 +26,6 @@ extern struct device *ux500_soc_device_init(const char *soc_id); + + extern void ux500_timer_init(void); + +-extern struct smp_operations ux500_smp_ops; + extern void ux500_cpu_die(unsigned int cpu); + + #endif /* __ASM_ARCH_SETUP_H */ +diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile +index 81151663ef38..3258174e6152 100644 +--- a/arch/arm64/Makefile ++++ b/arch/arm64/Makefile +@@ -31,7 +31,7 @@ endif + CHECKFLAGS += -D__aarch64__ + + ifeq ($(CONFIG_ARM64_ERRATUM_843419), y) +-CFLAGS_MODULE += -mcmodel=large ++KBUILD_CFLAGS_MODULE += -mcmodel=large + endif + + # Default value +diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h +index 56283f8a675c..cf7319422768 100644 +--- a/arch/arm64/include/asm/pgtable.h ++++ b/arch/arm64/include/asm/pgtable.h +@@ -80,7 +80,7 @@ extern void __pgd_error(const char *file, int line, unsigned long val); + #define PAGE_S2 __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY) + #define PAGE_S2_DEVICE __pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_UXN) + +-#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_PXN | PTE_UXN) ++#define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_PXN | PTE_UXN) + #define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) + #define PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE) + #define PAGE_COPY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) +@@ -460,7 +460,7 @@ static inline pud_t *pud_offset(pgd_t *pgd, unsigned long addr) + static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) + { + const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY | +- PTE_PROT_NONE | PTE_WRITE | PTE_TYPE_MASK; ++ PTE_PROT_NONE | PTE_VALID | PTE_WRITE; + pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); + return pte; + } +diff --git a/arch/sparc/crypto/aes_glue.c b/arch/sparc/crypto/aes_glue.c +index 2e48eb8813ff..c90930de76ba 100644 +--- a/arch/sparc/crypto/aes_glue.c ++++ b/arch/sparc/crypto/aes_glue.c +@@ -433,6 +433,7 @@ static struct crypto_alg algs[] = { { + .blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, + .setkey = aes_set_key, + .encrypt = cbc_encrypt, + .decrypt = cbc_decrypt, +@@ -452,6 +453,7 @@ static struct crypto_alg algs[] = { { + .blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, ++ .ivsize = AES_BLOCK_SIZE, + .setkey = aes_set_key, + .encrypt = ctr_crypt, + .decrypt = ctr_crypt, +diff --git a/arch/sparc/crypto/camellia_glue.c b/arch/sparc/crypto/camellia_glue.c +index 6bf2479a12fb..561a84d93cf6 100644 +--- a/arch/sparc/crypto/camellia_glue.c ++++ b/arch/sparc/crypto/camellia_glue.c +@@ -274,6 +274,7 @@ static struct crypto_alg algs[] = { { + .blkcipher = { + .min_keysize = CAMELLIA_MIN_KEY_SIZE, + .max_keysize = CAMELLIA_MAX_KEY_SIZE, ++ .ivsize = CAMELLIA_BLOCK_SIZE, + .setkey = camellia_set_key, + .encrypt = cbc_encrypt, + .decrypt = cbc_decrypt, +diff --git a/arch/sparc/crypto/des_glue.c b/arch/sparc/crypto/des_glue.c +index dd6a34fa6e19..61af794aa2d3 100644 +--- a/arch/sparc/crypto/des_glue.c ++++ b/arch/sparc/crypto/des_glue.c +@@ -429,6 +429,7 @@ static struct crypto_alg algs[] = { { + .blkcipher = { + .min_keysize = DES_KEY_SIZE, + .max_keysize = DES_KEY_SIZE, ++ .ivsize = DES_BLOCK_SIZE, + .setkey = des_set_key, + .encrypt = cbc_encrypt, + .decrypt = cbc_decrypt, +@@ -485,6 +486,7 @@ static struct crypto_alg algs[] = { { + .blkcipher = { + .min_keysize = DES3_EDE_KEY_SIZE, + .max_keysize = DES3_EDE_KEY_SIZE, ++ .ivsize = DES3_EDE_BLOCK_SIZE, + .setkey = des3_ede_set_key, + .encrypt = cbc3_encrypt, + .decrypt = cbc3_decrypt, +diff --git a/arch/x86/crypto/camellia_aesni_avx_glue.c b/arch/x86/crypto/camellia_aesni_avx_glue.c +index 80a0e4389c9a..bacaa13acac5 100644 +--- a/arch/x86/crypto/camellia_aesni_avx_glue.c ++++ b/arch/x86/crypto/camellia_aesni_avx_glue.c +@@ -554,6 +554,11 @@ static int __init camellia_aesni_init(void) + { + const char *feature_name; + ++ if (!cpu_has_avx || !cpu_has_aes || !cpu_has_osxsave) { ++ pr_info("AVX or AES-NI instructions are not detected.\n"); ++ return -ENODEV; ++ } ++ + if (!cpu_has_xfeatures(XSTATE_SSE | XSTATE_YMM, &feature_name)) { + pr_info("CPU feature '%s' is not supported.\n", feature_name); + return -ENODEV; +diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c +index e7a4fde5d631..2392541a96e6 100644 +--- a/arch/x86/kvm/emulate.c ++++ b/arch/x86/kvm/emulate.c +@@ -2418,7 +2418,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase) + u64 val, cr0, cr4; + u32 base3; + u16 selector; +- int i; ++ int i, r; + + for (i = 0; i < 16; i++) + *reg_write(ctxt, i) = GET_SMSTATE(u64, smbase, 0x7ff8 - i * 8); +@@ -2460,13 +2460,17 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u64 smbase) + dt.address = GET_SMSTATE(u64, smbase, 0x7e68); + ctxt->ops->set_gdt(ctxt, &dt); + ++ r = rsm_enter_protected_mode(ctxt, cr0, cr4); ++ if (r != X86EMUL_CONTINUE) ++ return r; ++ + for (i = 0; i < 6; i++) { +- int r = rsm_load_seg_64(ctxt, smbase, i); ++ r = rsm_load_seg_64(ctxt, smbase, i); + if (r != X86EMUL_CONTINUE) + return r; + } + +- return rsm_enter_protected_mode(ctxt, cr0, cr4); ++ return X86EMUL_CONTINUE; + } + + static int em_rsm(struct x86_emulate_ctxt *ctxt) +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 32c6e6ac5964..373328b71599 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -6706,6 +6706,12 @@ static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu) + return 1; + } + ++static inline bool kvm_vcpu_running(struct kvm_vcpu *vcpu) ++{ ++ return (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE && ++ !vcpu->arch.apf.halted); ++} ++ + static int vcpu_run(struct kvm_vcpu *vcpu) + { + int r; +@@ -6714,8 +6720,7 @@ static int vcpu_run(struct kvm_vcpu *vcpu) + vcpu->srcu_idx = srcu_read_lock(&kvm->srcu); + + for (;;) { +- if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE && +- !vcpu->arch.apf.halted) ++ if (kvm_vcpu_running(vcpu)) + r = vcpu_enter_guest(vcpu); + else + r = vcpu_block(kvm, vcpu); +@@ -8011,19 +8016,36 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm, + kvm_mmu_invalidate_zap_all_pages(kvm); + } + ++static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu) ++{ ++ if (!list_empty_careful(&vcpu->async_pf.done)) ++ return true; ++ ++ if (kvm_apic_has_events(vcpu)) ++ return true; ++ ++ if (vcpu->arch.pv.pv_unhalted) ++ return true; ++ ++ if (atomic_read(&vcpu->arch.nmi_queued)) ++ return true; ++ ++ if (test_bit(KVM_REQ_SMI, &vcpu->requests)) ++ return true; ++ ++ if (kvm_arch_interrupt_allowed(vcpu) && ++ kvm_cpu_has_interrupt(vcpu)) ++ return true; ++ ++ return false; ++} ++ + int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) + { + if (is_guest_mode(vcpu) && kvm_x86_ops->check_nested_events) + kvm_x86_ops->check_nested_events(vcpu, false); + +- return (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE && +- !vcpu->arch.apf.halted) +- || !list_empty_careful(&vcpu->async_pf.done) +- || kvm_apic_has_events(vcpu) +- || vcpu->arch.pv.pv_unhalted +- || atomic_read(&vcpu->arch.nmi_queued) || +- (kvm_arch_interrupt_allowed(vcpu) && +- kvm_cpu_has_interrupt(vcpu)); ++ return kvm_vcpu_running(vcpu) || kvm_vcpu_has_events(vcpu); + } + + int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +diff --git a/crypto/ahash.c b/crypto/ahash.c +index 8acb886032ae..9c1dc8d6106a 100644 +--- a/crypto/ahash.c ++++ b/crypto/ahash.c +@@ -544,7 +544,8 @@ static int ahash_prepare_alg(struct ahash_alg *alg) + struct crypto_alg *base = &alg->halg.base; + + if (alg->halg.digestsize > PAGE_SIZE / 8 || +- alg->halg.statesize > PAGE_SIZE / 8) ++ alg->halg.statesize > PAGE_SIZE / 8 || ++ alg->halg.statesize == 0) + return -EINVAL; + + base->cra_type = &crypto_ahash_type; +diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c +index bc67a93aa4f4..324bf35ec4dd 100644 +--- a/drivers/block/rbd.c ++++ b/drivers/block/rbd.c +@@ -5201,7 +5201,6 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev) + out_err: + if (parent) { + rbd_dev_unparent(rbd_dev); +- kfree(rbd_dev->header_name); + rbd_dev_destroy(parent); + } else { + rbd_put_client(rbdc); +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +index b16b9256883e..4c4035fdeb6f 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +@@ -76,8 +76,6 @@ static void amdgpu_flip_work_func(struct work_struct *__work) + /* We borrow the event spin lock for protecting flip_status */ + spin_lock_irqsave(&crtc->dev->event_lock, flags); + +- /* set the proper interrupt */ +- amdgpu_irq_get(adev, &adev->pageflip_irq, work->crtc_id); + /* do the flip (mmio) */ + adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base); + /* set the flip status */ +diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +index 82e8d0730517..a1a35a5df8e7 100644 +--- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c ++++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +@@ -6185,6 +6185,11 @@ static int ci_dpm_late_init(void *handle) + if (!amdgpu_dpm) + return 0; + ++ /* init the sysfs and debugfs files late */ ++ ret = amdgpu_pm_sysfs_init(adev); ++ if (ret) ++ return ret; ++ + ret = ci_set_temperature_range(adev); + if (ret) + return ret; +@@ -6232,9 +6237,6 @@ static int ci_dpm_sw_init(void *handle) + adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps; + if (amdgpu_dpm == 1) + amdgpu_pm_print_power_states(adev); +- ret = amdgpu_pm_sysfs_init(adev); +- if (ret) +- goto dpm_failed; + mutex_unlock(&adev->pm.mutex); + DRM_INFO("amdgpu: dpm initialized\n"); + +diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c +index 341c56681841..519fa515c4d8 100644 +--- a/drivers/gpu/drm/amd/amdgpu/cik.c ++++ b/drivers/gpu/drm/amd/amdgpu/cik.c +@@ -1565,6 +1565,9 @@ static void cik_pcie_gen3_enable(struct amdgpu_device *adev) + int ret, i; + u16 tmp16; + ++ if (pci_is_root_bus(adev->pdev->bus)) ++ return; ++ + if (amdgpu_pcie_gen2 == 0) + return; + +diff --git a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c +index ace870afc7d4..fd29c18fc14e 100644 +--- a/drivers/gpu/drm/amd/amdgpu/cz_dpm.c ++++ b/drivers/gpu/drm/amd/amdgpu/cz_dpm.c +@@ -596,6 +596,12 @@ static int cz_dpm_late_init(void *handle) + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + + if (amdgpu_dpm) { ++ int ret; ++ /* init the sysfs and debugfs files late */ ++ ret = amdgpu_pm_sysfs_init(adev); ++ if (ret) ++ return ret; ++ + /* powerdown unused blocks for now */ + cz_dpm_powergate_uvd(adev, true); + cz_dpm_powergate_vce(adev, true); +@@ -632,10 +638,6 @@ static int cz_dpm_sw_init(void *handle) + if (amdgpu_dpm == 1) + amdgpu_pm_print_power_states(adev); + +- ret = amdgpu_pm_sysfs_init(adev); +- if (ret) +- goto dpm_init_failed; +- + mutex_unlock(&adev->pm.mutex); + DRM_INFO("amdgpu: dpm initialized\n"); + +diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +index e774a437dd65..ef36467c7e34 100644 +--- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +@@ -233,6 +233,24 @@ static u32 dce_v10_0_vblank_get_counter(struct amdgpu_device *adev, int crtc) + return RREG32(mmCRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]); + } + ++static void dce_v10_0_pageflip_interrupt_init(struct amdgpu_device *adev) ++{ ++ unsigned i; ++ ++ /* Enable pflip interrupts */ ++ for (i = 0; i < adev->mode_info.num_crtc; i++) ++ amdgpu_irq_get(adev, &adev->pageflip_irq, i); ++} ++ ++static void dce_v10_0_pageflip_interrupt_fini(struct amdgpu_device *adev) ++{ ++ unsigned i; ++ ++ /* Disable pflip interrupts */ ++ for (i = 0; i < adev->mode_info.num_crtc; i++) ++ amdgpu_irq_put(adev, &adev->pageflip_irq, i); ++} ++ + /** + * dce_v10_0_page_flip - pageflip callback. + * +@@ -2641,9 +2659,10 @@ static void dce_v10_0_crtc_dpms(struct drm_crtc *crtc, int mode) + dce_v10_0_vga_enable(crtc, true); + amdgpu_atombios_crtc_blank(crtc, ATOM_DISABLE); + dce_v10_0_vga_enable(crtc, false); +- /* Make sure VBLANK interrupt is still enabled */ ++ /* Make sure VBLANK and PFLIP interrupts are still enabled */ + type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id); + amdgpu_irq_update(adev, &adev->crtc_irq, type); ++ amdgpu_irq_update(adev, &adev->pageflip_irq, type); + drm_vblank_post_modeset(dev, amdgpu_crtc->crtc_id); + dce_v10_0_crtc_load_lut(crtc); + break; +@@ -3002,6 +3021,8 @@ static int dce_v10_0_hw_init(void *handle) + dce_v10_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); + } + ++ dce_v10_0_pageflip_interrupt_init(adev); ++ + return 0; + } + +@@ -3016,6 +3037,8 @@ static int dce_v10_0_hw_fini(void *handle) + dce_v10_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); + } + ++ dce_v10_0_pageflip_interrupt_fini(adev); ++ + return 0; + } + +@@ -3027,6 +3050,8 @@ static int dce_v10_0_suspend(void *handle) + + dce_v10_0_hpd_fini(adev); + ++ dce_v10_0_pageflip_interrupt_fini(adev); ++ + return 0; + } + +@@ -3052,6 +3077,8 @@ static int dce_v10_0_resume(void *handle) + /* initialize hpd */ + dce_v10_0_hpd_init(adev); + ++ dce_v10_0_pageflip_interrupt_init(adev); ++ + return 0; + } + +@@ -3346,7 +3373,6 @@ static int dce_v10_0_pageflip_irq(struct amdgpu_device *adev, + spin_unlock_irqrestore(&adev->ddev->event_lock, flags); + + drm_vblank_put(adev->ddev, amdgpu_crtc->crtc_id); +- amdgpu_irq_put(adev, &adev->pageflip_irq, crtc_id); + queue_work(amdgpu_crtc->pflip_queue, &works->unpin_work); + + return 0; +diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +index c4a21a7afd68..329bca0f1331 100644 +--- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +@@ -233,6 +233,24 @@ static u32 dce_v11_0_vblank_get_counter(struct amdgpu_device *adev, int crtc) + return RREG32(mmCRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]); + } + ++static void dce_v11_0_pageflip_interrupt_init(struct amdgpu_device *adev) ++{ ++ unsigned i; ++ ++ /* Enable pflip interrupts */ ++ for (i = 0; i < adev->mode_info.num_crtc; i++) ++ amdgpu_irq_get(adev, &adev->pageflip_irq, i); ++} ++ ++static void dce_v11_0_pageflip_interrupt_fini(struct amdgpu_device *adev) ++{ ++ unsigned i; ++ ++ /* Disable pflip interrupts */ ++ for (i = 0; i < adev->mode_info.num_crtc; i++) ++ amdgpu_irq_put(adev, &adev->pageflip_irq, i); ++} ++ + /** + * dce_v11_0_page_flip - pageflip callback. + * +@@ -2640,9 +2658,10 @@ static void dce_v11_0_crtc_dpms(struct drm_crtc *crtc, int mode) + dce_v11_0_vga_enable(crtc, true); + amdgpu_atombios_crtc_blank(crtc, ATOM_DISABLE); + dce_v11_0_vga_enable(crtc, false); +- /* Make sure VBLANK interrupt is still enabled */ ++ /* Make sure VBLANK and PFLIP interrupts are still enabled */ + type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id); + amdgpu_irq_update(adev, &adev->crtc_irq, type); ++ amdgpu_irq_update(adev, &adev->pageflip_irq, type); + drm_vblank_post_modeset(dev, amdgpu_crtc->crtc_id); + dce_v11_0_crtc_load_lut(crtc); + break; +@@ -2888,7 +2907,7 @@ static int dce_v11_0_early_init(void *handle) + + switch (adev->asic_type) { + case CHIP_CARRIZO: +- adev->mode_info.num_crtc = 4; ++ adev->mode_info.num_crtc = 3; + adev->mode_info.num_hpd = 6; + adev->mode_info.num_dig = 9; + break; +@@ -3000,6 +3019,8 @@ static int dce_v11_0_hw_init(void *handle) + dce_v11_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); + } + ++ dce_v11_0_pageflip_interrupt_init(adev); ++ + return 0; + } + +@@ -3014,6 +3035,8 @@ static int dce_v11_0_hw_fini(void *handle) + dce_v11_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); + } + ++ dce_v11_0_pageflip_interrupt_fini(adev); ++ + return 0; + } + +@@ -3025,6 +3048,8 @@ static int dce_v11_0_suspend(void *handle) + + dce_v11_0_hpd_fini(adev); + ++ dce_v11_0_pageflip_interrupt_fini(adev); ++ + return 0; + } + +@@ -3051,6 +3076,8 @@ static int dce_v11_0_resume(void *handle) + /* initialize hpd */ + dce_v11_0_hpd_init(adev); + ++ dce_v11_0_pageflip_interrupt_init(adev); ++ + return 0; + } + +@@ -3345,7 +3372,6 @@ static int dce_v11_0_pageflip_irq(struct amdgpu_device *adev, + spin_unlock_irqrestore(&adev->ddev->event_lock, flags); + + drm_vblank_put(adev->ddev, amdgpu_crtc->crtc_id); +- amdgpu_irq_put(adev, &adev->pageflip_irq, crtc_id); + queue_work(amdgpu_crtc->pflip_queue, &works->unpin_work); + + return 0; +diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +index cc050a329c49..937879ed86bc 100644 +--- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c ++++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +@@ -204,6 +204,24 @@ static u32 dce_v8_0_vblank_get_counter(struct amdgpu_device *adev, int crtc) + return RREG32(mmCRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]); + } + ++static void dce_v8_0_pageflip_interrupt_init(struct amdgpu_device *adev) ++{ ++ unsigned i; ++ ++ /* Enable pflip interrupts */ ++ for (i = 0; i < adev->mode_info.num_crtc; i++) ++ amdgpu_irq_get(adev, &adev->pageflip_irq, i); ++} ++ ++static void dce_v8_0_pageflip_interrupt_fini(struct amdgpu_device *adev) ++{ ++ unsigned i; ++ ++ /* Disable pflip interrupts */ ++ for (i = 0; i < adev->mode_info.num_crtc; i++) ++ amdgpu_irq_put(adev, &adev->pageflip_irq, i); ++} ++ + /** + * dce_v8_0_page_flip - pageflip callback. + * +@@ -2575,9 +2593,10 @@ static void dce_v8_0_crtc_dpms(struct drm_crtc *crtc, int mode) + dce_v8_0_vga_enable(crtc, true); + amdgpu_atombios_crtc_blank(crtc, ATOM_DISABLE); + dce_v8_0_vga_enable(crtc, false); +- /* Make sure VBLANK interrupt is still enabled */ ++ /* Make sure VBLANK and PFLIP interrupts are still enabled */ + type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id); + amdgpu_irq_update(adev, &adev->crtc_irq, type); ++ amdgpu_irq_update(adev, &adev->pageflip_irq, type); + drm_vblank_post_modeset(dev, amdgpu_crtc->crtc_id); + dce_v8_0_crtc_load_lut(crtc); + break; +@@ -2933,6 +2952,8 @@ static int dce_v8_0_hw_init(void *handle) + dce_v8_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); + } + ++ dce_v8_0_pageflip_interrupt_init(adev); ++ + return 0; + } + +@@ -2947,6 +2968,8 @@ static int dce_v8_0_hw_fini(void *handle) + dce_v8_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false); + } + ++ dce_v8_0_pageflip_interrupt_fini(adev); ++ + return 0; + } + +@@ -2958,6 +2981,8 @@ static int dce_v8_0_suspend(void *handle) + + dce_v8_0_hpd_fini(adev); + ++ dce_v8_0_pageflip_interrupt_fini(adev); ++ + return 0; + } + +@@ -2981,6 +3006,8 @@ static int dce_v8_0_resume(void *handle) + /* initialize hpd */ + dce_v8_0_hpd_init(adev); + ++ dce_v8_0_pageflip_interrupt_init(adev); ++ + return 0; + } + +@@ -3376,7 +3403,6 @@ static int dce_v8_0_pageflip_irq(struct amdgpu_device *adev, + spin_unlock_irqrestore(&adev->ddev->event_lock, flags); + + drm_vblank_put(adev->ddev, amdgpu_crtc->crtc_id); +- amdgpu_irq_put(adev, &adev->pageflip_irq, crtc_id); + queue_work(amdgpu_crtc->pflip_queue, &works->unpin_work); + + return 0; +diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +index 94ec04a9c4d5..9745ed3a9aef 100644 +--- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c ++++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +@@ -2995,6 +2995,12 @@ static int kv_dpm_late_init(void *handle) + { + /* powerdown unused blocks for now */ + struct amdgpu_device *adev = (struct amdgpu_device *)handle; ++ int ret; ++ ++ /* init the sysfs and debugfs files late */ ++ ret = amdgpu_pm_sysfs_init(adev); ++ if (ret) ++ return ret; + + kv_dpm_powergate_acp(adev, true); + kv_dpm_powergate_samu(adev, true); +@@ -3038,9 +3044,6 @@ static int kv_dpm_sw_init(void *handle) + adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps; + if (amdgpu_dpm == 1) + amdgpu_pm_print_power_states(adev); +- ret = amdgpu_pm_sysfs_init(adev); +- if (ret) +- goto dpm_failed; + mutex_unlock(&adev->pm.mutex); + DRM_INFO("amdgpu: dpm initialized\n"); + +diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c +index 4f58a1e18de6..9ffa56cebdbc 100644 +--- a/drivers/gpu/drm/amd/amdgpu/vi.c ++++ b/drivers/gpu/drm/amd/amdgpu/vi.c +@@ -968,6 +968,9 @@ static void vi_pcie_gen3_enable(struct amdgpu_device *adev) + u32 mask; + int ret; + ++ if (pci_is_root_bus(adev->pdev->bus)) ++ return; ++ + if (amdgpu_pcie_gen2 == 0) + return; + +diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c +index 969e7898a7ed..27a2426c3daa 100644 +--- a/drivers/gpu/drm/drm_dp_mst_topology.c ++++ b/drivers/gpu/drm/drm_dp_mst_topology.c +@@ -2789,12 +2789,13 @@ static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs + if (msgs[num - 1].flags & I2C_M_RD) + reading = true; + +- if (!reading) { ++ if (!reading || (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS)) { + DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n"); + ret = -EIO; + goto out; + } + ++ memset(&msg, 0, sizeof(msg)); + msg.req_type = DP_REMOTE_I2C_READ; + msg.u.i2c_read.num_transactions = num - 1; + msg.u.i2c_read.port_number = port->port_num; +diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c +index 0f6cd33b531f..684bd4a13843 100644 +--- a/drivers/gpu/drm/drm_sysfs.c ++++ b/drivers/gpu/drm/drm_sysfs.c +@@ -235,18 +235,12 @@ static ssize_t dpms_show(struct device *device, + char *buf) + { + struct drm_connector *connector = to_drm_connector(device); +- struct drm_device *dev = connector->dev; +- uint64_t dpms_status; +- int ret; ++ int dpms; + +- ret = drm_object_property_get_value(&connector->base, +- dev->mode_config.dpms_property, +- &dpms_status); +- if (ret) +- return 0; ++ dpms = READ_ONCE(connector->dpms); + + return snprintf(buf, PAGE_SIZE, "%s\n", +- drm_get_dpms_name((int)dpms_status)); ++ drm_get_dpms_name(dpms)); + } + + static ssize_t enabled_show(struct device *device, +diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c +index 6751553abe4a..567791b27d6d 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c ++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c +@@ -178,8 +178,30 @@ nouveau_fbcon_sync(struct fb_info *info) + return 0; + } + ++static int ++nouveau_fbcon_open(struct fb_info *info, int user) ++{ ++ struct nouveau_fbdev *fbcon = info->par; ++ struct nouveau_drm *drm = nouveau_drm(fbcon->dev); ++ int ret = pm_runtime_get_sync(drm->dev->dev); ++ if (ret < 0 && ret != -EACCES) ++ return ret; ++ return 0; ++} ++ ++static int ++nouveau_fbcon_release(struct fb_info *info, int user) ++{ ++ struct nouveau_fbdev *fbcon = info->par; ++ struct nouveau_drm *drm = nouveau_drm(fbcon->dev); ++ pm_runtime_put(drm->dev->dev); ++ return 0; ++} ++ + static struct fb_ops nouveau_fbcon_ops = { + .owner = THIS_MODULE, ++ .fb_open = nouveau_fbcon_open, ++ .fb_release = nouveau_fbcon_release, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = nouveau_fbcon_fillrect, +@@ -195,6 +217,8 @@ static struct fb_ops nouveau_fbcon_ops = { + + static struct fb_ops nouveau_fbcon_sw_ops = { + .owner = THIS_MODULE, ++ .fb_open = nouveau_fbcon_open, ++ .fb_release = nouveau_fbcon_release, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = cfb_fillrect, +diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c +index 6b6e57e8c2d6..847a902e7385 100644 +--- a/drivers/gpu/drm/qxl/qxl_fb.c ++++ b/drivers/gpu/drm/qxl/qxl_fb.c +@@ -144,14 +144,17 @@ static void qxl_dirty_update(struct qxl_fbdev *qfbdev, + + spin_lock_irqsave(&qfbdev->dirty.lock, flags); + +- if (qfbdev->dirty.y1 < y) +- y = qfbdev->dirty.y1; +- if (qfbdev->dirty.y2 > y2) +- y2 = qfbdev->dirty.y2; +- if (qfbdev->dirty.x1 < x) +- x = qfbdev->dirty.x1; +- if (qfbdev->dirty.x2 > x2) +- x2 = qfbdev->dirty.x2; ++ if ((qfbdev->dirty.y2 - qfbdev->dirty.y1) && ++ (qfbdev->dirty.x2 - qfbdev->dirty.x1)) { ++ if (qfbdev->dirty.y1 < y) ++ y = qfbdev->dirty.y1; ++ if (qfbdev->dirty.y2 > y2) ++ y2 = qfbdev->dirty.y2; ++ if (qfbdev->dirty.x1 < x) ++ x = qfbdev->dirty.x1; ++ if (qfbdev->dirty.x2 > x2) ++ x2 = qfbdev->dirty.x2; ++ } + + qfbdev->dirty.x1 = x; + qfbdev->dirty.x2 = x2; +diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c +index d2e9e9efc159..6743174acdbc 100644 +--- a/drivers/gpu/drm/radeon/radeon_display.c ++++ b/drivers/gpu/drm/radeon/radeon_display.c +@@ -1633,18 +1633,8 @@ int radeon_modeset_init(struct radeon_device *rdev) + radeon_fbdev_init(rdev); + drm_kms_helper_poll_init(rdev->ddev); + +- if (rdev->pm.dpm_enabled) { +- /* do dpm late init */ +- ret = radeon_pm_late_init(rdev); +- if (ret) { +- rdev->pm.dpm_enabled = false; +- DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n"); +- } +- /* set the dpm state for PX since there won't be +- * a modeset to call this. +- */ +- radeon_pm_compute_clocks(rdev); +- } ++ /* do pm late init */ ++ ret = radeon_pm_late_init(rdev); + + return 0; + } +diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c +index 257b10be5cda..42986130cc63 100644 +--- a/drivers/gpu/drm/radeon/radeon_dp_mst.c ++++ b/drivers/gpu/drm/radeon/radeon_dp_mst.c +@@ -283,6 +283,7 @@ static struct drm_connector *radeon_dp_add_mst_connector(struct drm_dp_mst_topol + radeon_connector->mst_encoder = radeon_dp_create_fake_mst_encoder(master); + + drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0); ++ drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0); + drm_mode_connector_set_path_property(connector, pathprop); + drm_reinit_primary_mode_group(dev); + +diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c +index c1ba83a8dd8c..948c33105801 100644 +--- a/drivers/gpu/drm/radeon/radeon_pm.c ++++ b/drivers/gpu/drm/radeon/radeon_pm.c +@@ -1331,14 +1331,6 @@ static int radeon_pm_init_old(struct radeon_device *rdev) + INIT_DELAYED_WORK(&rdev->pm.dynpm_idle_work, radeon_dynpm_idle_work_handler); + + if (rdev->pm.num_power_states > 1) { +- /* where's the best place to put these? */ +- ret = device_create_file(rdev->dev, &dev_attr_power_profile); +- if (ret) +- DRM_ERROR("failed to create device file for power profile\n"); +- ret = device_create_file(rdev->dev, &dev_attr_power_method); +- if (ret) +- DRM_ERROR("failed to create device file for power method\n"); +- + if (radeon_debugfs_pm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for PM!\n"); + } +@@ -1396,20 +1388,6 @@ static int radeon_pm_init_dpm(struct radeon_device *rdev) + goto dpm_failed; + rdev->pm.dpm_enabled = true; + +- ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); +- if (ret) +- DRM_ERROR("failed to create device file for dpm state\n"); +- ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); +- if (ret) +- DRM_ERROR("failed to create device file for dpm state\n"); +- /* XXX: these are noops for dpm but are here for backwards compat */ +- ret = device_create_file(rdev->dev, &dev_attr_power_profile); +- if (ret) +- DRM_ERROR("failed to create device file for power profile\n"); +- ret = device_create_file(rdev->dev, &dev_attr_power_method); +- if (ret) +- DRM_ERROR("failed to create device file for power method\n"); +- + if (radeon_debugfs_pm_init(rdev)) { + DRM_ERROR("Failed to register debugfs file for dpm!\n"); + } +@@ -1550,9 +1528,44 @@ int radeon_pm_late_init(struct radeon_device *rdev) + int ret = 0; + + if (rdev->pm.pm_method == PM_METHOD_DPM) { +- mutex_lock(&rdev->pm.mutex); +- ret = radeon_dpm_late_enable(rdev); +- mutex_unlock(&rdev->pm.mutex); ++ if (rdev->pm.dpm_enabled) { ++ ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); ++ if (ret) ++ DRM_ERROR("failed to create device file for dpm state\n"); ++ ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); ++ if (ret) ++ DRM_ERROR("failed to create device file for dpm state\n"); ++ /* XXX: these are noops for dpm but are here for backwards compat */ ++ ret = device_create_file(rdev->dev, &dev_attr_power_profile); ++ if (ret) ++ DRM_ERROR("failed to create device file for power profile\n"); ++ ret = device_create_file(rdev->dev, &dev_attr_power_method); ++ if (ret) ++ DRM_ERROR("failed to create device file for power method\n"); ++ ++ mutex_lock(&rdev->pm.mutex); ++ ret = radeon_dpm_late_enable(rdev); ++ mutex_unlock(&rdev->pm.mutex); ++ if (ret) { ++ rdev->pm.dpm_enabled = false; ++ DRM_ERROR("radeon_pm_late_init failed, disabling dpm\n"); ++ } else { ++ /* set the dpm state for PX since there won't be ++ * a modeset to call this. ++ */ ++ radeon_pm_compute_clocks(rdev); ++ } ++ } ++ } else { ++ if (rdev->pm.num_power_states > 1) { ++ /* where's the best place to put these? */ ++ ret = device_create_file(rdev->dev, &dev_attr_power_profile); ++ if (ret) ++ DRM_ERROR("failed to create device file for power profile\n"); ++ ret = device_create_file(rdev->dev, &dev_attr_power_method); ++ if (ret) ++ DRM_ERROR("failed to create device file for power method\n"); ++ } + } + return ret; + } +diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c +index 3dd2de31a2f8..472b88285c75 100644 +--- a/drivers/i2c/busses/i2c-designware-platdrv.c ++++ b/drivers/i2c/busses/i2c-designware-platdrv.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -51,6 +52,22 @@ static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev) + } + + #ifdef CONFIG_ACPI ++/* ++ * The HCNT/LCNT information coming from ACPI should be the most accurate ++ * for given platform. However, some systems get it wrong. On such systems ++ * we get better results by calculating those based on the input clock. ++ */ ++static const struct dmi_system_id dw_i2c_no_acpi_params[] = { ++ { ++ .ident = "Dell Inspiron 7348", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7348"), ++ }, ++ }, ++ { } ++}; ++ + static void dw_i2c_acpi_params(struct platform_device *pdev, char method[], + u16 *hcnt, u16 *lcnt, u32 *sda_hold) + { +@@ -58,6 +75,9 @@ static void dw_i2c_acpi_params(struct platform_device *pdev, char method[], + acpi_handle handle = ACPI_HANDLE(&pdev->dev); + union acpi_object *obj; + ++ if (dmi_check_system(dw_i2c_no_acpi_params)) ++ return; ++ + if (ACPI_FAILURE(acpi_evaluate_object(handle, method, NULL, &buf))) + return; + +@@ -253,12 +273,6 @@ static int dw_i2c_probe(struct platform_device *pdev) + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + +- r = i2c_add_numbered_adapter(adap); +- if (r) { +- dev_err(&pdev->dev, "failure adding adapter\n"); +- return r; +- } +- + if (dev->pm_runtime_disabled) { + pm_runtime_forbid(&pdev->dev); + } else { +@@ -268,6 +282,13 @@ static int dw_i2c_probe(struct platform_device *pdev) + pm_runtime_enable(&pdev->dev); + } + ++ r = i2c_add_numbered_adapter(adap); ++ if (r) { ++ dev_err(&pdev->dev, "failure adding adapter\n"); ++ pm_runtime_disable(&pdev->dev); ++ return r; ++ } ++ + return 0; + } + +diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c +index d8361dada584..d8b5a8fee1e6 100644 +--- a/drivers/i2c/busses/i2c-rcar.c ++++ b/drivers/i2c/busses/i2c-rcar.c +@@ -690,15 +690,16 @@ static int rcar_i2c_probe(struct platform_device *pdev) + return ret; + } + ++ pm_runtime_enable(dev); ++ platform_set_drvdata(pdev, priv); ++ + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) { + dev_err(dev, "reg adap failed: %d\n", ret); ++ pm_runtime_disable(dev); + return ret; + } + +- pm_runtime_enable(dev); +- platform_set_drvdata(pdev, priv); +- + dev_info(dev, "probed\n"); + + return 0; +diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c +index 50bfd8cef5f2..5df819610d52 100644 +--- a/drivers/i2c/busses/i2c-s3c2410.c ++++ b/drivers/i2c/busses/i2c-s3c2410.c +@@ -1243,17 +1243,19 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) + i2c->adap.nr = i2c->pdata->bus_num; + i2c->adap.dev.of_node = pdev->dev.of_node; + ++ platform_set_drvdata(pdev, i2c); ++ ++ pm_runtime_enable(&pdev->dev); ++ + ret = i2c_add_numbered_adapter(&i2c->adap); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add bus to i2c core\n"); ++ pm_runtime_disable(&pdev->dev); + s3c24xx_i2c_deregister_cpufreq(i2c); + clk_unprepare(i2c->clk); + return ret; + } + +- platform_set_drvdata(pdev, i2c); +- +- pm_runtime_enable(&pdev->dev); + pm_runtime_enable(&i2c->adap.dev); + + dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); +diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c +index 75aef240c2d1..493c38e08bd2 100644 +--- a/drivers/md/dm-thin.c ++++ b/drivers/md/dm-thin.c +@@ -3255,7 +3255,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) + metadata_low_callback, + pool); + if (r) +- goto out_free_pt; ++ goto out_flags_changed; + + pt->callbacks.congested_fn = pool_is_congested; + dm_table_add_target_callbacks(ti->table, &pt->callbacks); +diff --git a/drivers/mfd/max77843.c b/drivers/mfd/max77843.c +index a354ac677ec7..1074a0d68680 100644 +--- a/drivers/mfd/max77843.c ++++ b/drivers/mfd/max77843.c +@@ -79,7 +79,7 @@ static int max77843_chg_init(struct max77843 *max77843) + if (!max77843->i2c_chg) { + dev_err(&max77843->i2c->dev, + "Cannot allocate I2C device for Charger\n"); +- return PTR_ERR(max77843->i2c_chg); ++ return -ENODEV; + } + i2c_set_clientdata(max77843->i2c_chg, max77843); + +diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h +index 28df37420da9..ac02c675c59c 100644 +--- a/drivers/net/ethernet/ibm/emac/core.h ++++ b/drivers/net/ethernet/ibm/emac/core.h +@@ -460,8 +460,8 @@ struct emac_ethtool_regs_subhdr { + u32 index; + }; + +-#define EMAC_ETHTOOL_REGS_VER 0 +-#define EMAC4_ETHTOOL_REGS_VER 1 +-#define EMAC4SYNC_ETHTOOL_REGS_VER 2 ++#define EMAC_ETHTOOL_REGS_VER 3 ++#define EMAC4_ETHTOOL_REGS_VER 4 ++#define EMAC4SYNC_ETHTOOL_REGS_VER 5 + + #endif /* __IBM_NEWEMAC_CORE_H */ +diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c +index 3837ae344f63..2ed75060da50 100644 +--- a/drivers/net/ppp/pppoe.c ++++ b/drivers/net/ppp/pppoe.c +@@ -313,7 +313,6 @@ static void pppoe_flush_dev(struct net_device *dev) + if (po->pppoe_dev == dev && + sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) { + pppox_unbind_sock(sk); +- sk->sk_state = PPPOX_ZOMBIE; + sk->sk_state_change(sk); + po->pppoe_dev = NULL; + dev_put(dev); +diff --git a/drivers/pinctrl/freescale/pinctrl-imx25.c b/drivers/pinctrl/freescale/pinctrl-imx25.c +index faf635654312..293ed4381cc0 100644 +--- a/drivers/pinctrl/freescale/pinctrl-imx25.c ++++ b/drivers/pinctrl/freescale/pinctrl-imx25.c +@@ -26,7 +26,8 @@ + #include "pinctrl-imx.h" + + enum imx25_pads { +- MX25_PAD_RESERVE0 = 1, ++ MX25_PAD_RESERVE0 = 0, ++ MX25_PAD_RESERVE1 = 1, + MX25_PAD_A10 = 2, + MX25_PAD_A13 = 3, + MX25_PAD_A14 = 4, +@@ -169,6 +170,7 @@ enum imx25_pads { + /* Pad names for the pinmux subsystem */ + static const struct pinctrl_pin_desc imx25_pinctrl_pads[] = { + IMX_PINCTRL_PIN(MX25_PAD_RESERVE0), ++ IMX_PINCTRL_PIN(MX25_PAD_RESERVE1), + IMX_PINCTRL_PIN(MX25_PAD_A10), + IMX_PINCTRL_PIN(MX25_PAD_A13), + IMX_PINCTRL_PIN(MX25_PAD_A14), +diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c +index 802fabb30e15..34cbe3505dac 100644 +--- a/fs/btrfs/backref.c ++++ b/fs/btrfs/backref.c +@@ -1809,7 +1809,6 @@ static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root, + int found = 0; + struct extent_buffer *eb; + struct btrfs_inode_extref *extref; +- struct extent_buffer *leaf; + u32 item_size; + u32 cur_offset; + unsigned long ptr; +@@ -1837,9 +1836,8 @@ static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root, + btrfs_set_lock_blocking_rw(eb, BTRFS_READ_LOCK); + btrfs_release_path(path); + +- leaf = path->nodes[0]; +- item_size = btrfs_item_size_nr(leaf, slot); +- ptr = btrfs_item_ptr_offset(leaf, slot); ++ item_size = btrfs_item_size_nr(eb, slot); ++ ptr = btrfs_item_ptr_offset(eb, slot); + cur_offset = 0; + + while (cur_offset < item_size) { +@@ -1853,7 +1851,7 @@ static int iterate_inode_extrefs(u64 inum, struct btrfs_root *fs_root, + if (ret) + break; + +- cur_offset += btrfs_inode_extref_name_len(leaf, extref); ++ cur_offset += btrfs_inode_extref_name_len(eb, extref); + cur_offset += sizeof(*extref); + } + btrfs_tree_read_unlock_blocking(eb); +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index 0770c91586ca..f490b6155091 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -4647,6 +4647,11 @@ locked: + bctl->flags |= BTRFS_BALANCE_TYPE_MASK; + } + ++ if (bctl->flags & ~(BTRFS_BALANCE_ARGS_MASK | BTRFS_BALANCE_TYPE_MASK)) { ++ ret = -EINVAL; ++ goto out_bargs; ++ } ++ + do_balance: + /* + * Ownership of bctl and mutually_exclusive_operation_running +diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h +index 95842a909e7f..2ac5f8cd701a 100644 +--- a/fs/btrfs/volumes.h ++++ b/fs/btrfs/volumes.h +@@ -376,6 +376,14 @@ struct map_lookup { + #define BTRFS_BALANCE_ARGS_VRANGE (1ULL << 4) + #define BTRFS_BALANCE_ARGS_LIMIT (1ULL << 5) + ++#define BTRFS_BALANCE_ARGS_MASK \ ++ (BTRFS_BALANCE_ARGS_PROFILES | \ ++ BTRFS_BALANCE_ARGS_USAGE | \ ++ BTRFS_BALANCE_ARGS_DEVID | \ ++ BTRFS_BALANCE_ARGS_DRANGE | \ ++ BTRFS_BALANCE_ARGS_VRANGE | \ ++ BTRFS_BALANCE_ARGS_LIMIT) ++ + /* + * Profile changing flags. When SOFT is set we won't relocate chunk if + * it already has the target profile (even though it may be +diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c +index cdefaa331a07..c29d9421bd5e 100644 +--- a/fs/nfsd/blocklayout.c ++++ b/fs/nfsd/blocklayout.c +@@ -56,14 +56,6 @@ nfsd4_block_proc_layoutget(struct inode *inode, const struct svc_fh *fhp, + u32 device_generation = 0; + int error; + +- /* +- * We do not attempt to support I/O smaller than the fs block size, +- * or not aligned to it. +- */ +- if (args->lg_minlength < block_size) { +- dprintk("pnfsd: I/O too small\n"); +- goto out_layoutunavailable; +- } + if (seg->offset & (block_size - 1)) { + dprintk("pnfsd: I/O misaligned\n"); + goto out_layoutunavailable; +diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h +index 86d0b25ed054..a89f505c856b 100644 +--- a/include/drm/drm_dp_mst_helper.h ++++ b/include/drm/drm_dp_mst_helper.h +@@ -253,6 +253,7 @@ struct drm_dp_remote_dpcd_write { + u8 *bytes; + }; + ++#define DP_REMOTE_I2C_READ_MAX_TRANSACTIONS 4 + struct drm_dp_remote_i2c_read { + u8 num_transactions; + u8 port_number; +@@ -262,7 +263,7 @@ struct drm_dp_remote_i2c_read { + u8 *bytes; + u8 no_stop_bit; + u8 i2c_transaction_delay; +- } transactions[4]; ++ } transactions[DP_REMOTE_I2C_READ_MAX_TRANSACTIONS]; + u8 read_i2c_device_id; + u8 num_bytes_read; + }; +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index 9b88536487e6..275158803824 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -2601,6 +2601,9 @@ static inline void skb_postpull_rcsum(struct sk_buff *skb, + { + if (skb->ip_summed == CHECKSUM_COMPLETE) + skb->csum = csum_sub(skb->csum, csum_partial(start, len, 0)); ++ else if (skb->ip_summed == CHECKSUM_PARTIAL && ++ skb_checksum_start_offset(skb) < 0) ++ skb->ip_summed = CHECKSUM_NONE; + } + + unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len); +diff --git a/include/net/af_unix.h b/include/net/af_unix.h +index 4a167b30a12f..cb1b9bbda332 100644 +--- a/include/net/af_unix.h ++++ b/include/net/af_unix.h +@@ -63,7 +63,11 @@ struct unix_sock { + #define UNIX_GC_MAYBE_CYCLE 1 + struct socket_wq peer_wq; + }; +-#define unix_sk(__sk) ((struct unix_sock *)__sk) ++ ++static inline struct unix_sock *unix_sk(struct sock *sk) ++{ ++ return (struct unix_sock *)sk; ++} + + #define peer_wait peer_wq.wait + +diff --git a/include/net/sock.h b/include/net/sock.h +index f21f0708ec59..4ca4c3fe446f 100644 +--- a/include/net/sock.h ++++ b/include/net/sock.h +@@ -826,6 +826,14 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s + if (sk_rcvqueues_full(sk, limit)) + return -ENOBUFS; + ++ /* ++ * If the skb was allocated from pfmemalloc reserves, only ++ * allow SOCK_MEMALLOC sockets to use it as this socket is ++ * helping free memory ++ */ ++ if (skb_pfmemalloc(skb) && !sock_flag(sk, SOCK_MEMALLOC)) ++ return -ENOMEM; ++ + __sk_add_backlog(sk, skb); + sk->sk_backlog.len += skb->truesize; + return 0; +diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c +index a20d4110e871..3688f1e07ebd 100644 +--- a/kernel/time/timekeeping.c ++++ b/kernel/time/timekeeping.c +@@ -1244,7 +1244,7 @@ void __init timekeeping_init(void) + set_normalized_timespec64(&tmp, -boot.tv_sec, -boot.tv_nsec); + tk_set_wall_to_mono(tk, tmp); + +- timekeeping_update(tk, TK_MIRROR); ++ timekeeping_update(tk, TK_MIRROR | TK_CLOCK_WAS_SET); + + write_seqcount_end(&tk_core.seq); + raw_spin_unlock_irqrestore(&timekeeper_lock, flags); +diff --git a/kernel/workqueue.c b/kernel/workqueue.c +index a413acb59a07..1de0f5fabb98 100644 +--- a/kernel/workqueue.c ++++ b/kernel/workqueue.c +@@ -1458,13 +1458,13 @@ static void __queue_delayed_work(int cpu, struct workqueue_struct *wq, + timer_stats_timer_set_start_info(&dwork->timer); + + dwork->wq = wq; ++ /* timer isn't guaranteed to run in this cpu, record earlier */ ++ if (cpu == WORK_CPU_UNBOUND) ++ cpu = raw_smp_processor_id(); + dwork->cpu = cpu; + timer->expires = jiffies + delay; + +- if (unlikely(cpu != WORK_CPU_UNBOUND)) +- add_timer_on(timer, cpu); +- else +- add_timer(timer); ++ add_timer_on(timer, cpu); + } + + /** +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index 237d4686482d..03a6f7506cf3 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -3687,6 +3687,7 @@ static int __mem_cgroup_usage_register_event(struct mem_cgroup *memcg, + ret = page_counter_memparse(args, "-1", &threshold); + if (ret) + return ret; ++ threshold <<= PAGE_SHIFT; + + mutex_lock(&memcg->thresholds_lock); + +diff --git a/net/core/ethtool.c b/net/core/ethtool.c +index b495ab1797fa..29edf74846fc 100644 +--- a/net/core/ethtool.c ++++ b/net/core/ethtool.c +@@ -1284,7 +1284,7 @@ static int ethtool_get_strings(struct net_device *dev, void __user *useraddr) + + gstrings.len = ret; + +- data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER); ++ data = kcalloc(gstrings.len, ETH_GSTRING_LEN, GFP_USER); + if (!data) + return -ENOMEM; + +diff --git a/net/core/filter.c b/net/core/filter.c +index be3098fb65e4..8dcdd86b68dd 100644 +--- a/net/core/filter.c ++++ b/net/core/filter.c +@@ -1412,6 +1412,7 @@ static u64 bpf_clone_redirect(u64 r1, u64 ifindex, u64 flags, u64 r4, u64 r5) + return dev_forward_skb(dev, skb2); + + skb2->dev = dev; ++ skb_sender_cpu_clear(skb2); + return dev_queue_xmit(skb2); + } + +@@ -1701,9 +1702,13 @@ int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, + goto out; + + /* We're copying the filter that has been originally attached, +- * so no conversion/decode needed anymore. ++ * so no conversion/decode needed anymore. eBPF programs that ++ * have no original program cannot be dumped through this. + */ ++ ret = -EACCES; + fprog = filter->prog->orig_prog; ++ if (!fprog) ++ goto out; + + ret = fprog->len; + if (!len) +diff --git a/net/core/skbuff.c b/net/core/skbuff.c +index 7b84330e5d30..7bfa18746681 100644 +--- a/net/core/skbuff.c ++++ b/net/core/skbuff.c +@@ -2958,11 +2958,12 @@ EXPORT_SYMBOL_GPL(skb_append_pagefrags); + */ + unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len) + { ++ unsigned char *data = skb->data; ++ + BUG_ON(len > skb->len); +- skb->len -= len; +- BUG_ON(skb->len < skb->data_len); +- skb_postpull_rcsum(skb, skb->data, len); +- return skb->data += len; ++ __skb_pull(skb, len); ++ skb_postpull_rcsum(skb, data, len); ++ return skb->data; + } + EXPORT_SYMBOL_GPL(skb_pull_rcsum); + +diff --git a/net/dsa/slave.c b/net/dsa/slave.c +index 35c47ddd04f0..25dbb91e1bc0 100644 +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -348,12 +348,17 @@ static int dsa_slave_stp_update(struct net_device *dev, u8 state) + static int dsa_slave_port_attr_set(struct net_device *dev, + struct switchdev_attr *attr) + { +- int ret = 0; ++ struct dsa_slave_priv *p = netdev_priv(dev); ++ struct dsa_switch *ds = p->parent; ++ int ret; + + switch (attr->id) { + case SWITCHDEV_ATTR_PORT_STP_STATE: +- if (attr->trans == SWITCHDEV_TRANS_COMMIT) +- ret = dsa_slave_stp_update(dev, attr->u.stp_state); ++ if (attr->trans == SWITCHDEV_TRANS_PREPARE) ++ ret = ds->drv->port_stp_update ? 0 : -EOPNOTSUPP; ++ else ++ ret = ds->drv->port_stp_update(ds, p->port, ++ attr->u.stp_state); + break; + default: + ret = -EOPNOTSUPP; +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index 134957159c27..61b45a17fc73 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -577,21 +577,22 @@ EXPORT_SYMBOL(inet_rtx_syn_ack); + static bool reqsk_queue_unlink(struct request_sock_queue *queue, + struct request_sock *req) + { +- struct listen_sock *lopt = queue->listen_opt; + struct request_sock **prev; ++ struct listen_sock *lopt; + bool found = false; + + spin_lock(&queue->syn_wait_lock); +- +- for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; +- prev = &(*prev)->dl_next) { +- if (*prev == req) { +- *prev = req->dl_next; +- found = true; +- break; ++ lopt = queue->listen_opt; ++ if (lopt) { ++ for (prev = &lopt->syn_table[req->rsk_hash]; *prev != NULL; ++ prev = &(*prev)->dl_next) { ++ if (*prev == req) { ++ *prev = req->dl_next; ++ found = true; ++ break; ++ } + } + } +- + spin_unlock(&queue->syn_wait_lock); + if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer)) + reqsk_put(req); +@@ -685,20 +686,20 @@ void reqsk_queue_hash_req(struct request_sock_queue *queue, + req->num_timeout = 0; + req->sk = NULL; + ++ setup_timer(&req->rsk_timer, reqsk_timer_handler, (unsigned long)req); ++ mod_timer_pinned(&req->rsk_timer, jiffies + timeout); ++ req->rsk_hash = hash; ++ + /* before letting lookups find us, make sure all req fields + * are committed to memory and refcnt initialized. + */ + smp_wmb(); + atomic_set(&req->rsk_refcnt, 2); +- setup_timer(&req->rsk_timer, reqsk_timer_handler, (unsigned long)req); +- req->rsk_hash = hash; + + spin_lock(&queue->syn_wait_lock); + req->dl_next = lopt->syn_table[hash]; + lopt->syn_table[hash] = req; + spin_unlock(&queue->syn_wait_lock); +- +- mod_timer_pinned(&req->rsk_timer, jiffies + timeout); + } + EXPORT_SYMBOL(reqsk_queue_hash_req); + +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 00b64d402a57..dd6ebba5846c 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -139,6 +139,9 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) + struct net_device *loopback_dev = net->loopback_dev; + int cpu; + ++ if (dev == loopback_dev) ++ return; ++ + for_each_possible_cpu(cpu) { + struct uncached_list *ul = per_cpu_ptr(&rt6_uncached_list, cpu); + struct rt6_info *rt; +@@ -148,14 +151,12 @@ static void rt6_uncached_list_flush_dev(struct net *net, struct net_device *dev) + struct inet6_dev *rt_idev = rt->rt6i_idev; + struct net_device *rt_dev = rt->dst.dev; + +- if (rt_idev && (rt_idev->dev == dev || !dev) && +- rt_idev->dev != loopback_dev) { ++ if (rt_idev->dev == dev) { + rt->rt6i_idev = in6_dev_get(loopback_dev); + in6_dev_put(rt_idev); + } + +- if (rt_dev && (rt_dev == dev || !dev) && +- rt_dev != loopback_dev) { ++ if (rt_dev == dev) { + rt->dst.dev = loopback_dev; + dev_hold(rt->dst.dev); + dev_put(rt_dev); +@@ -2577,7 +2578,8 @@ void rt6_ifdown(struct net *net, struct net_device *dev) + + fib6_clean_all(net, fib6_ifdown, &adn); + icmp6_clean_all(fib6_ifdown, &adn); +- rt6_uncached_list_flush_dev(net, dev); ++ if (dev) ++ rt6_uncached_list_flush_dev(net, dev); + } + + struct rt6_mtu_change_arg { +diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c +index f6b090df3930..afca2eb4dfa7 100644 +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -1319,7 +1319,7 @@ static void l2tp_tunnel_del_work(struct work_struct *work) + tunnel = container_of(work, struct l2tp_tunnel, del_work); + sk = l2tp_tunnel_sock_lookup(tunnel); + if (!sk) +- return; ++ goto out; + + sock = sk->sk_socket; + +@@ -1341,6 +1341,8 @@ static void l2tp_tunnel_del_work(struct work_struct *work) + } + + l2tp_tunnel_sock_put(sk); ++out: ++ l2tp_tunnel_dec_refcount(tunnel); + } + + /* Create a socket for the tunnel, if one isn't set up by +@@ -1636,8 +1638,13 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); + */ + int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) + { ++ l2tp_tunnel_inc_refcount(tunnel); + l2tp_tunnel_closeall(tunnel); +- return (false == queue_work(l2tp_wq, &tunnel->del_work)); ++ if (false == queue_work(l2tp_wq, &tunnel->del_work)) { ++ l2tp_tunnel_dec_refcount(tunnel); ++ return 1; ++ } ++ return 0; + } + EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index 0857f7243797..a133d16eb053 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -2750,6 +2750,7 @@ static int netlink_dump(struct sock *sk) + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh; + int len, err = -ENOBUFS; ++ int alloc_min_size; + int alloc_size; + + mutex_lock(nlk->cb_mutex); +@@ -2758,9 +2759,6 @@ static int netlink_dump(struct sock *sk) + goto errout_skb; + } + +- cb = &nlk->cb; +- alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); +- + if (!netlink_rx_is_mmaped(sk) && + atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) + goto errout_skb; +@@ -2770,23 +2768,35 @@ static int netlink_dump(struct sock *sk) + * to reduce number of system calls on dump operations, if user + * ever provided a big enough buffer. + */ +- if (alloc_size < nlk->max_recvmsg_len) { +- skb = netlink_alloc_skb(sk, +- nlk->max_recvmsg_len, +- nlk->portid, ++ cb = &nlk->cb; ++ alloc_min_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE); ++ ++ if (alloc_min_size < nlk->max_recvmsg_len) { ++ alloc_size = nlk->max_recvmsg_len; ++ skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, + GFP_KERNEL | + __GFP_NOWARN | + __GFP_NORETRY); +- /* available room should be exact amount to avoid MSG_TRUNC */ +- if (skb) +- skb_reserve(skb, skb_tailroom(skb) - +- nlk->max_recvmsg_len); + } +- if (!skb) ++ if (!skb) { ++ alloc_size = alloc_min_size; + skb = netlink_alloc_skb(sk, alloc_size, nlk->portid, + GFP_KERNEL); ++ } + if (!skb) + goto errout_skb; ++ ++ /* Trim skb to allocated size. User is expected to provide buffer as ++ * large as max(min_dump_alloc, 16KiB (mac_recvmsg_len capped at ++ * netlink_recvmsg())). dump will pack as many smaller messages as ++ * could fit within the allocated skb. skb is typically allocated ++ * with larger space than required (could be as much as near 2x the ++ * requested size with align to next power of 2 approach). Allowing ++ * dump to use the excess space makes it difficult for a user to have a ++ * reasonable static buffer based on the expected largest dump of a ++ * single netdev. The outcome is MSG_TRUNC error. ++ */ ++ skb_reserve(skb, skb_tailroom(skb) - alloc_size); + netlink_skb_set_owner_r(skb, sk); + + len = cb->dump(skb, cb); +diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c +index b5c3bba87fc8..af08e6fc9860 100644 +--- a/net/openvswitch/flow_table.c ++++ b/net/openvswitch/flow_table.c +@@ -92,7 +92,8 @@ struct sw_flow *ovs_flow_alloc(void) + + /* Initialize the default stat node. */ + stats = kmem_cache_alloc_node(flow_stats_cache, +- GFP_KERNEL | __GFP_ZERO, 0); ++ GFP_KERNEL | __GFP_ZERO, ++ node_online(0) ? 0 : NUMA_NO_NODE); + if (!stats) + goto err; + +diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c +index 268545050ddb..b1768198ad59 100644 +--- a/net/sched/act_mirred.c ++++ b/net/sched/act_mirred.c +@@ -168,6 +168,7 @@ static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a, + + skb2->skb_iif = skb->dev->ifindex; + skb2->dev = dev; ++ skb_sender_cpu_clear(skb2); + err = dev_queue_xmit(skb2); + + out: +diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +index 2e1348bde325..96d886a866e9 100644 +--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c ++++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +@@ -146,7 +146,8 @@ int rdma_read_chunk_lcl(struct svcxprt_rdma *xprt, + ctxt->read_hdr = head; + pages_needed = + min_t(int, pages_needed, rdma_read_max_sge(xprt, pages_needed)); +- read = min_t(int, pages_needed << PAGE_SHIFT, rs_length); ++ read = min_t(int, (pages_needed << PAGE_SHIFT) - *page_offset, ++ rs_length); + + for (pno = 0; pno < pages_needed; pno++) { + int len = min_t(int, rs_length, PAGE_SIZE - pg_off); +@@ -245,7 +246,8 @@ int rdma_read_chunk_frmr(struct svcxprt_rdma *xprt, + ctxt->direction = DMA_FROM_DEVICE; + ctxt->frmr = frmr; + pages_needed = min_t(int, pages_needed, xprt->sc_frmr_pg_list_len); +- read = min_t(int, pages_needed << PAGE_SHIFT, rs_length); ++ read = min_t(int, (pages_needed << PAGE_SHIFT) - *page_offset, ++ rs_length); + + frmr->kva = page_address(rqstp->rq_arg.pages[pg_no]); + frmr->direction = DMA_FROM_DEVICE; +diff --git a/net/tipc/msg.h b/net/tipc/msg.h +index 19c45fb66238..49f9a9648aa9 100644 +--- a/net/tipc/msg.h ++++ b/net/tipc/msg.h +@@ -357,7 +357,7 @@ static inline u32 msg_importance(struct tipc_msg *m) + if (likely((usr <= TIPC_CRITICAL_IMPORTANCE) && !msg_errcode(m))) + return usr; + if ((usr == MSG_FRAGMENTER) || (usr == MSG_BUNDLER)) +- return msg_bits(m, 5, 13, 0x7); ++ return msg_bits(m, 9, 0, 0x7); + return TIPC_SYSTEM_IMPORTANCE; + } + +@@ -366,7 +366,7 @@ static inline void msg_set_importance(struct tipc_msg *m, u32 i) + int usr = msg_user(m); + + if (likely((usr == MSG_FRAGMENTER) || (usr == MSG_BUNDLER))) +- msg_set_bits(m, 5, 13, 0x7, i); ++ msg_set_bits(m, 9, 0, 0x7, i); + else if (i < TIPC_SYSTEM_IMPORTANCE) + msg_set_user(m, i); + else +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index 03ee4d359f6a..94f658235fb4 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -2064,6 +2064,11 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state) + goto out; + } + ++ if (flags & MSG_PEEK) ++ skip = sk_peek_offset(sk, flags); ++ else ++ skip = 0; ++ + do { + int chunk; + struct sk_buff *skb, *last; +@@ -2112,7 +2117,6 @@ unlock: + break; + } + +- skip = sk_peek_offset(sk, flags); + while (skip >= unix_skb_len(skb)) { + skip -= unix_skb_len(skb); + last = skb; +@@ -2181,6 +2185,17 @@ unlock: + + sk_peek_offset_fwd(sk, chunk); + ++ if (UNIXCB(skb).fp) ++ break; ++ ++ skip = 0; ++ last = skb; ++ last_len = skb->len; ++ unix_state_lock(sk); ++ skb = skb_peek_next(skb, &sk->sk_receive_queue); ++ if (skb) ++ goto again; ++ unix_state_unlock(sk); + break; + } + } while (size); diff --git a/patch/kernel/odroid-next/patch-4.2.5-6.patch b/patch/kernel/odroid-next/patch-4.2.5-6.patch new file mode 100644 index 000000000..39cc3950e --- /dev/null +++ b/patch/kernel/odroid-next/patch-4.2.5-6.patch @@ -0,0 +1,3380 @@ +diff --git a/Makefile b/Makefile +index 96076dcad18e..9ef37399b4e8 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,6 +1,6 @@ + VERSION = 4 + PATCHLEVEL = 2 +-SUBLEVEL = 5 ++SUBLEVEL = 6 + EXTRAVERSION = + NAME = Hurr durr I'ma sheep + +diff --git a/arch/arm/boot/dts/am57xx-beagle-x15.dts b/arch/arm/boot/dts/am57xx-beagle-x15.dts +index a63bf78191ea..03385fabf839 100644 +--- a/arch/arm/boot/dts/am57xx-beagle-x15.dts ++++ b/arch/arm/boot/dts/am57xx-beagle-x15.dts +@@ -415,11 +415,12 @@ + /* SMPS9 unused */ + + ldo1_reg: ldo1 { +- /* VDD_SD */ ++ /* VDD_SD / VDDSHV8 */ + regulator-name = "ldo1"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; ++ regulator-always-on; + }; + + ldo2_reg: ldo2 { +diff --git a/arch/arm/boot/dts/armada-385-db-ap.dts b/arch/arm/boot/dts/armada-385-db-ap.dts +index 89f5a95954ed..4047621b137e 100644 +--- a/arch/arm/boot/dts/armada-385-db-ap.dts ++++ b/arch/arm/boot/dts/armada-385-db-ap.dts +@@ -46,7 +46,7 @@ + + / { + model = "Marvell Armada 385 Access Point Development Board"; +- compatible = "marvell,a385-db-ap", "marvell,armada385", "marvell,armada38x"; ++ compatible = "marvell,a385-db-ap", "marvell,armada385", "marvell,armada380"; + + chosen { + stdout-path = "serial1:115200n8"; +diff --git a/arch/arm/boot/dts/berlin2q.dtsi b/arch/arm/boot/dts/berlin2q.dtsi +index 63a48490e2f9..d4dbd28d348c 100644 +--- a/arch/arm/boot/dts/berlin2q.dtsi ++++ b/arch/arm/boot/dts/berlin2q.dtsi +@@ -152,7 +152,7 @@ + }; + + usb_phy2: phy@a2f400 { +- compatible = "marvell,berlin2-usb-phy"; ++ compatible = "marvell,berlin2cd-usb-phy"; + reg = <0xa2f400 0x128>; + #phy-cells = <0>; + resets = <&chip_rst 0x104 14>; +@@ -170,7 +170,7 @@ + }; + + usb_phy0: phy@b74000 { +- compatible = "marvell,berlin2-usb-phy"; ++ compatible = "marvell,berlin2cd-usb-phy"; + reg = <0xb74000 0x128>; + #phy-cells = <0>; + resets = <&chip_rst 0x104 12>; +@@ -178,7 +178,7 @@ + }; + + usb_phy1: phy@b78000 { +- compatible = "marvell,berlin2-usb-phy"; ++ compatible = "marvell,berlin2cd-usb-phy"; + reg = <0xb78000 0x128>; + #phy-cells = <0>; + resets = <&chip_rst 0x104 13>; +diff --git a/arch/arm/boot/dts/exynos5420-peach-pit.dts b/arch/arm/boot/dts/exynos5420-peach-pit.dts +index 8f4d76c5e11c..1b95da79293c 100644 +--- a/arch/arm/boot/dts/exynos5420-peach-pit.dts ++++ b/arch/arm/boot/dts/exynos5420-peach-pit.dts +@@ -915,6 +915,11 @@ + }; + }; + ++&pmu_system_controller { ++ assigned-clocks = <&pmu_system_controller 0>; ++ assigned-clock-parents = <&clock CLK_FIN_PLL>; ++}; ++ + &rtc { + status = "okay"; + clocks = <&clock CLK_RTC>, <&max77802 MAX77802_CLK_32K_AP>; +diff --git a/arch/arm/boot/dts/exynos5800-peach-pi.dts b/arch/arm/boot/dts/exynos5800-peach-pi.dts +index 7d5b386b5ae6..8f40c7e549bd 100644 +--- a/arch/arm/boot/dts/exynos5800-peach-pi.dts ++++ b/arch/arm/boot/dts/exynos5800-peach-pi.dts +@@ -878,6 +878,11 @@ + }; + }; + ++&pmu_system_controller { ++ assigned-clocks = <&pmu_system_controller 0>; ++ assigned-clock-parents = <&clock CLK_FIN_PLL>; ++}; ++ + &rtc { + status = "okay"; + clocks = <&clock CLK_RTC>, <&max77802 MAX77802_CLK_32K_AP>; +diff --git a/arch/arm/boot/dts/imx7d.dtsi b/arch/arm/boot/dts/imx7d.dtsi +index c42cf8db0451..9accbae15374 100644 +--- a/arch/arm/boot/dts/imx7d.dtsi ++++ b/arch/arm/boot/dts/imx7d.dtsi +@@ -340,10 +340,10 @@ + status = "disabled"; + }; + +- uart2: serial@30870000 { ++ uart2: serial@30890000 { + compatible = "fsl,imx7d-uart", + "fsl,imx6q-uart"; +- reg = <0x30870000 0x10000>; ++ reg = <0x30890000 0x10000>; + interrupts = ; + clocks = <&clks IMX7D_UART2_ROOT_CLK>, + <&clks IMX7D_UART2_ROOT_CLK>; +diff --git a/arch/arm/boot/dts/ste-hrefv60plus.dtsi b/arch/arm/boot/dts/ste-hrefv60plus.dtsi +index 810cda743b6d..9c2387b34d0c 100644 +--- a/arch/arm/boot/dts/ste-hrefv60plus.dtsi ++++ b/arch/arm/boot/dts/ste-hrefv60plus.dtsi +@@ -56,7 +56,7 @@ + /* VMMCI level-shifter enable */ + default_hrefv60_cfg2 { + pins = "GPIO169_D22"; +- ste,config = <&gpio_out_lo>; ++ ste,config = <&gpio_out_hi>; + }; + /* VMMCI level-shifter voltage select */ + default_hrefv60_cfg3 { +diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig +index bfb915d05665..dd5fc1e36384 100644 +--- a/arch/arm/kvm/Kconfig ++++ b/arch/arm/kvm/Kconfig +@@ -21,6 +21,7 @@ config KVM + depends on MMU && OF + select PREEMPT_NOTIFIERS + select ANON_INODES ++ select ARM_GIC + select HAVE_KVM_CPU_RELAX_INTERCEPT + select HAVE_KVM_ARCH_TLB_FLUSH_ALL + select KVM_MMIO +diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c +index 4a87e86dec45..7c21760f590f 100644 +--- a/arch/arm/mach-exynos/pm_domains.c ++++ b/arch/arm/mach-exynos/pm_domains.c +@@ -200,15 +200,15 @@ no_clk: + args.args_count = 0; + child_domain = of_genpd_get_from_provider(&args); + if (IS_ERR(child_domain)) +- goto next_pd; ++ continue; + + if (of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &args) != 0) +- goto next_pd; ++ continue; + + parent_domain = of_genpd_get_from_provider(&args); + if (IS_ERR(parent_domain)) +- goto next_pd; ++ continue; + + if (pm_genpd_add_subdomain(parent_domain, child_domain)) + pr_warn("%s failed to add subdomain: %s\n", +@@ -216,8 +216,6 @@ no_clk: + else + pr_info("%s has as child subdomain: %s.\n", + parent_domain->name, child_domain->name); +-next_pd: +- of_node_put(np); + } + + return 0; +diff --git a/arch/arm/plat-orion/common.c b/arch/arm/plat-orion/common.c +index 2235081a04ee..8861c367d061 100644 +--- a/arch/arm/plat-orion/common.c ++++ b/arch/arm/plat-orion/common.c +@@ -495,7 +495,7 @@ void __init orion_ge00_switch_init(struct dsa_platform_data *d, int irq) + + d->netdev = &orion_ge00.dev; + for (i = 0; i < d->nr_chips; i++) +- d->chip[i].host_dev = &orion_ge00_shared.dev; ++ d->chip[i].host_dev = &orion_ge_mvmdio.dev; + orion_switch_device.dev.platform_data = d; + + platform_device_register(&orion_switch_device); +diff --git a/arch/arm/vdso/vdsomunge.c b/arch/arm/vdso/vdsomunge.c +index aedec81d1198..f6455273b2f8 100644 +--- a/arch/arm/vdso/vdsomunge.c ++++ b/arch/arm/vdso/vdsomunge.c +@@ -45,7 +45,6 @@ + * it does. + */ + +-#include + #include + #include + #include +@@ -59,6 +58,16 @@ + #include + #include + ++#define swab16(x) \ ++ ((((x) & 0x00ff) << 8) | \ ++ (((x) & 0xff00) >> 8)) ++ ++#define swab32(x) \ ++ ((((x) & 0x000000ff) << 24) | \ ++ (((x) & 0x0000ff00) << 8) | \ ++ (((x) & 0x00ff0000) >> 8) | \ ++ (((x) & 0xff000000) >> 24)) ++ + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define HOST_ORDER ELFDATA2LSB + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +@@ -104,17 +113,17 @@ static void cleanup(void) + + static Elf32_Word read_elf_word(Elf32_Word word, bool swap) + { +- return swap ? bswap_32(word) : word; ++ return swap ? swab32(word) : word; + } + + static Elf32_Half read_elf_half(Elf32_Half half, bool swap) + { +- return swap ? bswap_16(half) : half; ++ return swap ? swab16(half) : half; + } + + static void write_elf_word(Elf32_Word val, Elf32_Word *dst, bool swap) + { +- *dst = swap ? bswap_32(val) : val; ++ *dst = swap ? swab32(val) : val; + } + + int main(int argc, char **argv) +diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c +index 7922c2e710ca..7ac3920b1356 100644 +--- a/arch/arm64/kernel/armv8_deprecated.c ++++ b/arch/arm64/kernel/armv8_deprecated.c +@@ -279,22 +279,24 @@ static void register_insn_emulation_sysctl(struct ctl_table *table) + */ + #define __user_swpX_asm(data, addr, res, temp, B) \ + __asm__ __volatile__( \ +- " mov %w2, %w1\n" \ +- "0: ldxr"B" %w1, [%3]\n" \ +- "1: stxr"B" %w0, %w2, [%3]\n" \ ++ "0: ldxr"B" %w2, [%3]\n" \ ++ "1: stxr"B" %w0, %w1, [%3]\n" \ + " cbz %w0, 2f\n" \ + " mov %w0, %w4\n" \ ++ " b 3f\n" \ + "2:\n" \ ++ " mov %w1, %w2\n" \ ++ "3:\n" \ + " .pushsection .fixup,\"ax\"\n" \ + " .align 2\n" \ +- "3: mov %w0, %w5\n" \ +- " b 2b\n" \ ++ "4: mov %w0, %w5\n" \ ++ " b 3b\n" \ + " .popsection" \ + " .pushsection __ex_table,\"a\"\n" \ + " .align 3\n" \ +- " .quad 0b, 3b\n" \ +- " .quad 1b, 3b\n" \ +- " .popsection" \ ++ " .quad 0b, 4b\n" \ ++ " .quad 1b, 4b\n" \ ++ " .popsection\n" \ + : "=&r" (res), "+r" (data), "=&r" (temp) \ + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ + : "memory") +diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c +index 407991bf79f5..ccb6078ed9f2 100644 +--- a/arch/arm64/kernel/stacktrace.c ++++ b/arch/arm64/kernel/stacktrace.c +@@ -48,11 +48,7 @@ int notrace unwind_frame(struct stackframe *frame) + + frame->sp = fp + 0x10; + frame->fp = *(unsigned long *)(fp); +- /* +- * -4 here because we care about the PC at time of bl, +- * not where the return will go. +- */ +- frame->pc = *(unsigned long *)(fp + 8) - 4; ++ frame->pc = *(unsigned long *)(fp + 8); + + return 0; + } +diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c +index 8297d502217e..44ca4143b013 100644 +--- a/arch/arm64/kernel/suspend.c ++++ b/arch/arm64/kernel/suspend.c +@@ -80,17 +80,21 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) + if (ret == 0) { + /* + * We are resuming from reset with TTBR0_EL1 set to the +- * idmap to enable the MMU; restore the active_mm mappings in +- * TTBR0_EL1 unless the active_mm == &init_mm, in which case +- * the thread entered cpu_suspend with TTBR0_EL1 set to +- * reserved TTBR0 page tables and should be restored as such. ++ * idmap to enable the MMU; set the TTBR0 to the reserved ++ * page tables to prevent speculative TLB allocations, flush ++ * the local tlb and set the default tcr_el1.t0sz so that ++ * the TTBR0 address space set-up is properly restored. ++ * If the current active_mm != &init_mm we entered cpu_suspend ++ * with mappings in TTBR0 that must be restored, so we switch ++ * them back to complete the address space configuration ++ * restoration before returning. + */ +- if (mm == &init_mm) +- cpu_set_reserved_ttbr0(); +- else +- cpu_switch_mm(mm->pgd, mm); +- ++ cpu_set_reserved_ttbr0(); + flush_tlb_all(); ++ cpu_set_default_tcr_t0sz(); ++ ++ if (mm != &init_mm) ++ cpu_switch_mm(mm->pgd, mm); + + /* + * Restore per-cpu offset before any kernel +diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c +index caffb10e7aa3..5607693f35cf 100644 +--- a/arch/powerpc/kernel/rtas.c ++++ b/arch/powerpc/kernel/rtas.c +@@ -1041,6 +1041,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ++ if (!rtas.entry) ++ return -EINVAL; ++ + if (copy_from_user(&args, uargs, 3 * sizeof(u32)) != 0) + return -EFAULT; + +diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c +index 557232f758b6..5610b185d1e9 100644 +--- a/arch/um/kernel/trap.c ++++ b/arch/um/kernel/trap.c +@@ -220,7 +220,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, + show_regs(container_of(regs, struct pt_regs, regs)); + panic("Segfault with no mm"); + } +- else if (!is_user && address < TASK_SIZE) { ++ else if (!is_user && address > PAGE_SIZE && address < TASK_SIZE) { + show_regs(container_of(regs, struct pt_regs, regs)); + panic("Kernel tried to access user memory at addr 0x%lx, ip 0x%lx", + address, ip); +diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c +index 7d69afd8b6fa..16edc0f169fa 100644 +--- a/arch/x86/boot/compressed/eboot.c ++++ b/arch/x86/boot/compressed/eboot.c +@@ -667,6 +667,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto, + bool conout_found = false; + void *dummy = NULL; + u32 h = handles[i]; ++ u32 current_fb_base; + + status = efi_call_early(handle_protocol, h, + proto, (void **)&gop32); +@@ -678,7 +679,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto, + if (status == EFI_SUCCESS) + conout_found = true; + +- status = __gop_query32(gop32, &info, &size, &fb_base); ++ status = __gop_query32(gop32, &info, &size, ¤t_fb_base); + if (status == EFI_SUCCESS && (!first_gop || conout_found)) { + /* + * Systems that use the UEFI Console Splitter may +@@ -692,6 +693,7 @@ setup_gop32(struct screen_info *si, efi_guid_t *proto, + pixel_format = info->pixel_format; + pixel_info = info->pixel_information; + pixels_per_scan_line = info->pixels_per_scan_line; ++ fb_base = current_fb_base; + + /* + * Once we've found a GOP supporting ConOut, +@@ -770,6 +772,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, + bool conout_found = false; + void *dummy = NULL; + u64 h = handles[i]; ++ u32 current_fb_base; + + status = efi_call_early(handle_protocol, h, + proto, (void **)&gop64); +@@ -781,7 +784,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, + if (status == EFI_SUCCESS) + conout_found = true; + +- status = __gop_query64(gop64, &info, &size, &fb_base); ++ status = __gop_query64(gop64, &info, &size, ¤t_fb_base); + if (status == EFI_SUCCESS && (!first_gop || conout_found)) { + /* + * Systems that use the UEFI Console Splitter may +@@ -795,6 +798,7 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, + pixel_format = info->pixel_format; + pixel_info = info->pixel_information; + pixels_per_scan_line = info->pixels_per_scan_line; ++ fb_base = current_fb_base; + + /* + * Once we've found a GOP supporting ConOut, +diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c +index 5880b482d83c..11b46d91f4e5 100644 +--- a/arch/x86/kernel/apic/io_apic.c ++++ b/arch/x86/kernel/apic/io_apic.c +@@ -2547,7 +2547,9 @@ void __init setup_ioapic_dest(void) + mask = apic->target_cpus(); + + chip = irq_data_get_irq_chip(idata); +- chip->irq_set_affinity(idata, mask, false); ++ /* Might be lapic_chip for irq 0 */ ++ if (chip->irq_set_affinity) ++ chip->irq_set_affinity(idata, mask, false); + } + } + #endif +diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c +index 777ad2f03160..3cebc65221a2 100644 +--- a/arch/x86/xen/enlighten.c ++++ b/arch/x86/xen/enlighten.c +@@ -33,7 +33,7 @@ + #include + #include + +-#ifdef CONFIG_KEXEC_CORE ++#ifdef CONFIG_KEXEC + #include + #endif + +@@ -1804,7 +1804,7 @@ static struct notifier_block xen_hvm_cpu_notifier = { + .notifier_call = xen_hvm_cpu_notify, + }; + +-#ifdef CONFIG_KEXEC_CORE ++#ifdef CONFIG_KEXEC + static void xen_hvm_shutdown(void) + { + native_machine_shutdown(); +@@ -1838,7 +1838,7 @@ static void __init xen_hvm_guest_init(void) + x86_init.irqs.intr_init = xen_init_IRQ; + xen_hvm_init_time_ops(); + xen_hvm_init_mmu_ops(); +-#ifdef CONFIG_KEXEC_CORE ++#ifdef CONFIG_KEXEC + machine_ops.shutdown = xen_hvm_shutdown; + machine_ops.crash_shutdown = xen_hvm_crash_shutdown; + #endif +diff --git a/block/blk-core.c b/block/blk-core.c +index 627ed0c593fb..1955ed3a1fa9 100644 +--- a/block/blk-core.c ++++ b/block/blk-core.c +@@ -578,7 +578,7 @@ void blk_cleanup_queue(struct request_queue *q) + q->queue_lock = &q->__queue_lock; + spin_unlock_irq(lock); + +- bdi_destroy(&q->backing_dev_info); ++ bdi_unregister(&q->backing_dev_info); + + /* @q is and will stay empty, shutdown and put */ + blk_put_queue(q); +diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c +index 9115c6d59948..273519894951 100644 +--- a/block/blk-mq-tag.c ++++ b/block/blk-mq-tag.c +@@ -628,6 +628,7 @@ void blk_mq_free_tags(struct blk_mq_tags *tags) + { + bt_free(&tags->bitmap_tags); + bt_free(&tags->breserved_tags); ++ free_cpumask_var(tags->cpumask); + kfree(tags); + } + +diff --git a/block/blk-mq.c b/block/blk-mq.c +index c69902695136..4d6ff5259a61 100644 +--- a/block/blk-mq.c ++++ b/block/blk-mq.c +@@ -2263,10 +2263,8 @@ void blk_mq_free_tag_set(struct blk_mq_tag_set *set) + int i; + + for (i = 0; i < set->nr_hw_queues; i++) { +- if (set->tags[i]) { ++ if (set->tags[i]) + blk_mq_free_rq_map(set, set->tags[i], i); +- free_cpumask_var(set->tags[i]->cpumask); +- } + } + + kfree(set->tags); +diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c +index 6264b382d4d1..145ddb6c6d31 100644 +--- a/block/blk-sysfs.c ++++ b/block/blk-sysfs.c +@@ -502,6 +502,7 @@ static void blk_release_queue(struct kobject *kobj) + struct request_queue *q = + container_of(kobj, struct request_queue, kobj); + ++ bdi_exit(&q->backing_dev_info); + blkcg_exit_queue(q); + + if (q->elevator) { +diff --git a/crypto/ablkcipher.c b/crypto/ablkcipher.c +index b788f169cc98..b4ffc5be1a93 100644 +--- a/crypto/ablkcipher.c ++++ b/crypto/ablkcipher.c +@@ -706,7 +706,7 @@ struct crypto_ablkcipher *crypto_alloc_ablkcipher(const char *alg_name, + err: + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +diff --git a/crypto/algapi.c b/crypto/algapi.c +index 3c079b7f23f6..b603b34ce8a8 100644 +--- a/crypto/algapi.c ++++ b/crypto/algapi.c +@@ -335,7 +335,7 @@ static void crypto_wait_for_test(struct crypto_larval *larval) + crypto_alg_tested(larval->alg.cra_driver_name, 0); + } + +- err = wait_for_completion_interruptible(&larval->completion); ++ err = wait_for_completion_killable(&larval->completion); + WARN_ON(err); + + out: +diff --git a/crypto/api.c b/crypto/api.c +index afe4610afc4b..bbc147cb5dec 100644 +--- a/crypto/api.c ++++ b/crypto/api.c +@@ -172,7 +172,7 @@ static struct crypto_alg *crypto_larval_wait(struct crypto_alg *alg) + struct crypto_larval *larval = (void *)alg; + long timeout; + +- timeout = wait_for_completion_interruptible_timeout( ++ timeout = wait_for_completion_killable_timeout( + &larval->completion, 60 * HZ); + + alg = larval->adult; +@@ -445,7 +445,7 @@ struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32 mask) + err: + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +@@ -562,7 +562,7 @@ void *crypto_alloc_tfm(const char *alg_name, + err: + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c +index 08ea2867fc8a..d59fb4eeed2b 100644 +--- a/crypto/crypto_user.c ++++ b/crypto/crypto_user.c +@@ -376,7 +376,7 @@ static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type, + err = PTR_ERR(alg); + if (err != -EAGAIN) + break; +- if (signal_pending(current)) { ++ if (fatal_signal_pending(current)) { + err = -EINTR; + break; + } +diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c +index 7920c2741b47..cf91c114ed9f 100644 +--- a/drivers/block/nvme-core.c ++++ b/drivers/block/nvme-core.c +@@ -597,6 +597,7 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, + struct nvme_iod *iod = ctx; + struct request *req = iod_get_private(iod); + struct nvme_cmd_info *cmd_rq = blk_mq_rq_to_pdu(req); ++ bool requeue = false; + + u16 status = le16_to_cpup(&cqe->status) >> 1; + +@@ -605,12 +606,13 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, + && (jiffies - req->start_time) < req->timeout) { + unsigned long flags; + ++ requeue = true; + blk_mq_requeue_request(req); + spin_lock_irqsave(req->q->queue_lock, flags); + if (!blk_queue_stopped(req->q)) + blk_mq_kick_requeue_list(req->q); + spin_unlock_irqrestore(req->q->queue_lock, flags); +- return; ++ goto release_iod; + } + if (req->cmd_type == REQ_TYPE_DRV_PRIV) { + if (cmd_rq->ctx == CMD_CTX_CANCELLED) +@@ -631,7 +633,7 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, + dev_warn(nvmeq->dev->dev, + "completing aborted command with status:%04x\n", + status); +- ++ release_iod: + if (iod->nents) { + dma_unmap_sg(nvmeq->dev->dev, iod->sg, iod->nents, + rq_data_dir(req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); +@@ -644,7 +646,8 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, + } + nvme_free_iod(nvmeq->dev, iod); + +- blk_mq_complete_request(req); ++ if (likely(!requeue)) ++ blk_mq_complete_request(req); + } + + /* length is in bytes. gfp flags indicates whether we may sleep. */ +@@ -1764,7 +1767,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) + + length = (io.nblocks + 1) << ns->lba_shift; + meta_len = (io.nblocks + 1) * ns->ms; +- metadata = (void __user *)(unsigned long)io.metadata; ++ metadata = (void __user *)(uintptr_t)io.metadata; + write = io.opcode & 1; + + if (ns->ext) { +@@ -1804,7 +1807,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) + c.rw.metadata = cpu_to_le64(meta_dma); + + status = __nvme_submit_sync_cmd(ns->queue, &c, NULL, +- (void __user *)io.addr, length, NULL, 0); ++ (void __user *)(uintptr_t)io.addr, length, NULL, 0); + unmap: + if (meta) { + if (status == NVME_SC_SUCCESS && !write) { +@@ -1846,7 +1849,7 @@ static int nvme_user_cmd(struct nvme_dev *dev, struct nvme_ns *ns, + timeout = msecs_to_jiffies(cmd.timeout_ms); + + status = __nvme_submit_sync_cmd(ns ? ns->queue : dev->admin_q, &c, +- NULL, (void __user *)cmd.addr, cmd.data_len, ++ NULL, (void __user *)(uintptr_t)cmd.addr, cmd.data_len, + &cmd.result, timeout); + if (status >= 0) { + if (put_user(cmd.result, &ucmd->result)) +diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c +index 324bf35ec4dd..017b7d58ae06 100644 +--- a/drivers/block/rbd.c ++++ b/drivers/block/rbd.c +@@ -96,6 +96,8 @@ static int atomic_dec_return_safe(atomic_t *v) + #define RBD_MINORS_PER_MAJOR 256 + #define RBD_SINGLE_MAJOR_PART_SHIFT 4 + ++#define RBD_MAX_PARENT_CHAIN_LEN 16 ++ + #define RBD_SNAP_DEV_NAME_PREFIX "snap_" + #define RBD_MAX_SNAP_NAME_LEN \ + (NAME_MAX - (sizeof (RBD_SNAP_DEV_NAME_PREFIX) - 1)) +@@ -426,7 +428,7 @@ static ssize_t rbd_add_single_major(struct bus_type *bus, const char *buf, + size_t count); + static ssize_t rbd_remove_single_major(struct bus_type *bus, const char *buf, + size_t count); +-static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping); ++static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth); + static void rbd_spec_put(struct rbd_spec *spec); + + static int rbd_dev_id_to_minor(int dev_id) +@@ -3819,6 +3821,9 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) + q->limits.discard_zeroes_data = 1; + + blk_queue_merge_bvec(q, rbd_merge_bvec); ++ if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC)) ++ q->backing_dev_info.capabilities |= BDI_CAP_STABLE_WRITES; ++ + disk->queue = q; + + q->queuedata = rbd_dev; +@@ -5169,44 +5174,51 @@ out_err: + return ret; + } + +-static int rbd_dev_probe_parent(struct rbd_device *rbd_dev) ++/* ++ * @depth is rbd_dev_image_probe() -> rbd_dev_probe_parent() -> ++ * rbd_dev_image_probe() recursion depth, which means it's also the ++ * length of the already discovered part of the parent chain. ++ */ ++static int rbd_dev_probe_parent(struct rbd_device *rbd_dev, int depth) + { + struct rbd_device *parent = NULL; +- struct rbd_spec *parent_spec; +- struct rbd_client *rbdc; + int ret; + + if (!rbd_dev->parent_spec) + return 0; +- /* +- * We need to pass a reference to the client and the parent +- * spec when creating the parent rbd_dev. Images related by +- * parent/child relationships always share both. +- */ +- parent_spec = rbd_spec_get(rbd_dev->parent_spec); +- rbdc = __rbd_get_client(rbd_dev->rbd_client); + +- ret = -ENOMEM; +- parent = rbd_dev_create(rbdc, parent_spec, NULL); +- if (!parent) ++ if (++depth > RBD_MAX_PARENT_CHAIN_LEN) { ++ pr_info("parent chain is too long (%d)\n", depth); ++ ret = -EINVAL; + goto out_err; ++ } + +- ret = rbd_dev_image_probe(parent, false); ++ parent = rbd_dev_create(rbd_dev->rbd_client, rbd_dev->parent_spec, ++ NULL); ++ if (!parent) { ++ ret = -ENOMEM; ++ goto out_err; ++ } ++ ++ /* ++ * Images related by parent/child relationships always share ++ * rbd_client and spec/parent_spec, so bump their refcounts. ++ */ ++ __rbd_get_client(rbd_dev->rbd_client); ++ rbd_spec_get(rbd_dev->parent_spec); ++ ++ ret = rbd_dev_image_probe(parent, depth); + if (ret < 0) + goto out_err; ++ + rbd_dev->parent = parent; + atomic_set(&rbd_dev->parent_ref, 1); +- + return 0; ++ + out_err: +- if (parent) { +- rbd_dev_unparent(rbd_dev); ++ rbd_dev_unparent(rbd_dev); ++ if (parent) + rbd_dev_destroy(parent); +- } else { +- rbd_put_client(rbdc); +- rbd_spec_put(parent_spec); +- } +- + return ret; + } + +@@ -5324,7 +5336,7 @@ static void rbd_dev_image_release(struct rbd_device *rbd_dev) + * parent), initiate a watch on its header object before using that + * object to get detailed information about the rbd image. + */ +-static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) ++static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth) + { + int ret; + +@@ -5342,7 +5354,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + if (ret) + goto err_out_format; + +- if (mapping) { ++ if (!depth) { + ret = rbd_dev_header_watch_sync(rbd_dev); + if (ret) { + if (ret == -ENOENT) +@@ -5363,7 +5375,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + * Otherwise this is a parent image, identified by pool, image + * and snap ids - need to fill in names for those ids. + */ +- if (mapping) ++ if (!depth) + ret = rbd_spec_fill_snap_id(rbd_dev); + else + ret = rbd_spec_fill_names(rbd_dev); +@@ -5385,12 +5397,12 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + * Need to warn users if this image is the one being + * mapped and has a parent. + */ +- if (mapping && rbd_dev->parent_spec) ++ if (!depth && rbd_dev->parent_spec) + rbd_warn(rbd_dev, + "WARNING: kernel layering is EXPERIMENTAL!"); + } + +- ret = rbd_dev_probe_parent(rbd_dev); ++ ret = rbd_dev_probe_parent(rbd_dev, depth); + if (ret) + goto err_out_probe; + +@@ -5401,7 +5413,7 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping) + err_out_probe: + rbd_dev_unprobe(rbd_dev); + err_out_watch: +- if (mapping) ++ if (!depth) + rbd_dev_header_unwatch_sync(rbd_dev); + out_header_name: + kfree(rbd_dev->header_name); +@@ -5464,7 +5476,7 @@ static ssize_t do_rbd_add(struct bus_type *bus, + spec = NULL; /* rbd_dev now owns this */ + rbd_opts = NULL; /* rbd_dev now owns this */ + +- rc = rbd_dev_image_probe(rbd_dev, true); ++ rc = rbd_dev_image_probe(rbd_dev, 0); + if (rc < 0) + goto err_out_rbd_dev; + +diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c +index 7a8a73f1fc04..d68b08ae4be1 100644 +--- a/drivers/block/xen-blkfront.c ++++ b/drivers/block/xen-blkfront.c +@@ -1984,7 +1984,8 @@ static void blkback_changed(struct xenbus_device *dev, + break; + /* Missed the backend's Closing state -- fallthrough */ + case XenbusStateClosing: +- blkfront_closing(info); ++ if (info) ++ blkfront_closing(info); + break; + } + } +diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c +index 7d9879e166cf..395cb7f9f5a4 100644 +--- a/drivers/bus/arm-ccn.c ++++ b/drivers/bus/arm-ccn.c +@@ -1188,7 +1188,8 @@ static int arm_ccn_pmu_cpu_notifier(struct notifier_block *nb, + break; + perf_pmu_migrate_context(&dt->pmu, cpu, target); + cpumask_set_cpu(target, &dt->cpu); +- WARN_ON(irq_set_affinity(ccn->irq, &dt->cpu) != 0); ++ if (ccn->irq) ++ WARN_ON(irq_set_affinity(ccn->irq, &dt->cpu) != 0); + default: + break; + } +diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c +index c0eaf0973bd2..779b6ff0c7ad 100644 +--- a/drivers/clk/clkdev.c ++++ b/drivers/clk/clkdev.c +@@ -333,7 +333,8 @@ int clk_add_alias(const char *alias, const char *alias_dev_name, + if (IS_ERR(r)) + return PTR_ERR(r); + +- l = clkdev_create(r, alias, "%s", alias_dev_name); ++ l = clkdev_create(r, alias, alias_dev_name ? "%s" : NULL, ++ alias_dev_name); + clk_put(r); + + return l ? 0 : -ENODEV; +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index fcb929ec5304..aba2117a80c1 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -766,6 +766,11 @@ static inline void intel_pstate_sample(struct cpudata *cpu) + local_irq_save(flags); + rdmsrl(MSR_IA32_APERF, aperf); + rdmsrl(MSR_IA32_MPERF, mperf); ++ if (cpu->prev_mperf == mperf) { ++ local_irq_restore(flags); ++ return; ++ } ++ + tsc = native_read_tsc(); + local_irq_restore(flags); + +diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c +index ca7831168298..91cf71008e11 100644 +--- a/drivers/edac/sb_edac.c ++++ b/drivers/edac/sb_edac.c +@@ -1648,6 +1648,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, + { + struct sbridge_pvt *pvt = mci->pvt_info; + struct pci_dev *pdev; ++ u8 saw_chan_mask = 0; + int i; + + for (i = 0; i < sbridge_dev->n_devs; i++) { +@@ -1681,6 +1682,7 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, + { + int id = pdev->device - PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TAD0; + pvt->pci_tad[id] = pdev; ++ saw_chan_mask |= 1 << id; + } + break; + case PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_DDRIO: +@@ -1701,10 +1703,8 @@ static int sbridge_mci_bind_devs(struct mem_ctl_info *mci, + !pvt-> pci_tad || !pvt->pci_ras || !pvt->pci_ta) + goto enodev; + +- for (i = 0; i < NUM_CHANNELS; i++) { +- if (!pvt->pci_tad[i]) +- goto enodev; +- } ++ if (saw_chan_mask != 0x0f) ++ goto enodev; + return 0; + + enodev: +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h +index f7b49d5ce4b8..e3305a5aedfd 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h +@@ -1583,6 +1583,7 @@ struct amdgpu_pm { + u8 fan_max_rpm; + /* dpm */ + bool dpm_enabled; ++ bool sysfs_initialized; + struct amdgpu_dpm dpm; + const struct firmware *fw; /* SMC firmware */ + uint32_t fw_version; +diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +index ed13baa7c976..91c7556a365a 100644 +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c +@@ -693,6 +693,9 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) + { + int ret; + ++ if (adev->pm.sysfs_initialized) ++ return 0; ++ + if (adev->pm.funcs->get_temperature == NULL) + return 0; + adev->pm.int_hwmon_dev = hwmon_device_register_with_groups(adev->dev, +@@ -721,6 +724,8 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev) + return ret; + } + ++ adev->pm.sysfs_initialized = true; ++ + return 0; + } + +diff --git a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +index 9745ed3a9aef..7e9154c7f1db 100644 +--- a/drivers/gpu/drm/amd/amdgpu/kv_dpm.c ++++ b/drivers/gpu/drm/amd/amdgpu/kv_dpm.c +@@ -2997,6 +2997,9 @@ static int kv_dpm_late_init(void *handle) + struct amdgpu_device *adev = (struct amdgpu_device *)handle; + int ret; + ++ if (!amdgpu_dpm) ++ return 0; ++ + /* init the sysfs and debugfs files late */ + ret = amdgpu_pm_sysfs_init(adev); + if (ret) +diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c +index fed748311b92..4e8d72d40af4 100644 +--- a/drivers/gpu/drm/drm_crtc.c ++++ b/drivers/gpu/drm/drm_crtc.c +@@ -4221,7 +4221,7 @@ drm_property_create_blob(struct drm_device *dev, size_t length, + struct drm_property_blob *blob; + int ret; + +- if (!length) ++ if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) + return ERR_PTR(-EINVAL); + + blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); +@@ -4573,7 +4573,7 @@ int drm_mode_createblob_ioctl(struct drm_device *dev, + * not associated with any file_priv. */ + mutex_lock(&dev->mode_config.blob_lock); + out_resp->blob_id = blob->base.id; +- list_add_tail(&file_priv->blobs, &blob->head_file); ++ list_add_tail(&blob->head_file, &file_priv->blobs); + mutex_unlock(&dev->mode_config.blob_lock); + + return 0; +diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c +index 27a2426c3daa..1f94219f3e0e 100644 +--- a/drivers/gpu/drm/drm_dp_mst_topology.c ++++ b/drivers/gpu/drm/drm_dp_mst_topology.c +@@ -1193,17 +1193,18 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_ + + list_for_each_entry(port, &mstb->ports, next) { + if (port->port_num == port_num) { +- if (!port->mstb) { ++ mstb = port->mstb; ++ if (!mstb) { + DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]); +- return NULL; ++ goto out; + } + +- mstb = port->mstb; + break; + } + } + } + kref_get(&mstb->kref); ++out: + mutex_unlock(&mgr->lock); + return mstb; + } +diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c +index 8fd431bcdfd3..a96b9006a51e 100644 +--- a/drivers/gpu/drm/i915/i915_gem_userptr.c ++++ b/drivers/gpu/drm/i915/i915_gem_userptr.c +@@ -804,7 +804,10 @@ static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { + * Also note, that the object created here is not currently a "first class" + * object, in that several ioctls are banned. These are the CPU access + * ioctls: mmap(), pwrite and pread. In practice, you are expected to use +- * direct access via your pointer rather than use those ioctls. ++ * direct access via your pointer rather than use those ioctls. Another ++ * restriction is that we do not allow userptr surfaces to be pinned to the ++ * hardware and so we reject any attempt to create a framebuffer out of a ++ * userptr. + * + * If you think this is a good interface to use to pass GPU memory between + * drivers, please use dma-buf instead. In fact, wherever possible use +diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c +index 107c6c0519fd..10b1b657d32a 100644 +--- a/drivers/gpu/drm/i915/intel_display.c ++++ b/drivers/gpu/drm/i915/intel_display.c +@@ -1729,6 +1729,8 @@ static void i9xx_enable_pll(struct intel_crtc *crtc) + I915_READ(DPLL(!crtc->pipe)) | DPLL_DVO_2X_MODE); + } + ++ I915_WRITE(reg, dpll); ++ + /* Wait for the clocks to stabilize. */ + POSTING_READ(reg); + udelay(150); +@@ -14070,6 +14072,11 @@ static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + ++ if (obj->userptr.mm) { ++ DRM_DEBUG("attempting to use a userptr for a framebuffer, denied\n"); ++ return -EINVAL; ++ } ++ + return drm_gem_handle_create(file, &obj->base, handle); + } + +diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c +index 7f2161a1ff5d..504728b401b6 100644 +--- a/drivers/gpu/drm/i915/intel_lrc.c ++++ b/drivers/gpu/drm/i915/intel_lrc.c +@@ -1250,6 +1250,7 @@ static int gen8_emit_flush_render(struct intel_ringbuffer *ringbuf, + if (flush_domains) { + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; ++ flags |= PIPE_CONTROL_FLUSH_ENABLE; + } + + if (invalidate_domains) { +diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c +index 3817a6f00d9e..ba672aa980e1 100644 +--- a/drivers/gpu/drm/i915/intel_ringbuffer.c ++++ b/drivers/gpu/drm/i915/intel_ringbuffer.c +@@ -342,6 +342,7 @@ gen7_render_ring_flush(struct intel_engine_cs *ring, + if (flush_domains) { + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; ++ flags |= PIPE_CONTROL_FLUSH_ENABLE; + } + if (invalidate_domains) { + flags |= PIPE_CONTROL_TLB_INVALIDATE; +@@ -412,6 +413,7 @@ gen8_render_ring_flush(struct intel_engine_cs *ring, + if (flush_domains) { + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; ++ flags |= PIPE_CONTROL_FLUSH_ENABLE; + } + if (invalidate_domains) { + flags |= PIPE_CONTROL_TLB_INVALIDATE; +diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c +index af1ee517f372..0b2239423a37 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_gem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c +@@ -227,11 +227,12 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, + struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nvkm_vma *vma; + +- if (nvbo->bo.mem.mem_type == TTM_PL_TT) ++ if (is_power_of_2(nvbo->valid_domains)) ++ rep->domain = nvbo->valid_domains; ++ else if (nvbo->bo.mem.mem_type == TTM_PL_TT) + rep->domain = NOUVEAU_GEM_DOMAIN_GART; + else + rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; +- + rep->offset = nvbo->bo.offset; + if (cli->vm) { + vma = nouveau_bo_vma_find(nvbo, cli->vm); +diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c +index 65adb9c72377..bb292143997e 100644 +--- a/drivers/gpu/drm/radeon/atombios_encoders.c ++++ b/drivers/gpu/drm/radeon/atombios_encoders.c +@@ -237,6 +237,7 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, + backlight_update_status(bd); + + DRM_INFO("radeon atom DIG backlight initialized\n"); ++ rdev->mode_info.bl_encoder = radeon_encoder; + + return; + +@@ -1624,9 +1625,14 @@ radeon_atom_encoder_dpms_avivo(struct drm_encoder *encoder, int mode) + } else + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { +- struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; ++ if (rdev->mode_info.bl_encoder) { ++ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + +- atombios_set_backlight_level(radeon_encoder, dig->backlight_level); ++ atombios_set_backlight_level(radeon_encoder, dig->backlight_level); ++ } else { ++ args.ucAction = ATOM_LCD_BLON; ++ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); ++ } + } + break; + case DRM_MODE_DPMS_STANDBY: +@@ -1706,8 +1712,13 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode) + if (ASIC_IS_DCE4(rdev)) + atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0); + } +- if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) +- atombios_set_backlight_level(radeon_encoder, dig->backlight_level); ++ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { ++ if (rdev->mode_info.bl_encoder) ++ atombios_set_backlight_level(radeon_encoder, dig->backlight_level); ++ else ++ atombios_dig_transmitter_setup(encoder, ++ ATOM_TRANSMITTER_ACTION_LCD_BLON, 0, 0); ++ } + if (ext_encoder) + atombios_external_encoder_setup(encoder, ext_encoder, ATOM_ENABLE); + break; +diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h +index f03b7eb15233..b6cbd816537e 100644 +--- a/drivers/gpu/drm/radeon/radeon.h ++++ b/drivers/gpu/drm/radeon/radeon.h +@@ -1658,6 +1658,7 @@ struct radeon_pm { + u8 fan_max_rpm; + /* dpm */ + bool dpm_enabled; ++ bool sysfs_initialized; + struct radeon_dpm dpm; + }; + +diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c +index ef99917f000d..c6ee80216cf4 100644 +--- a/drivers/gpu/drm/radeon/radeon_encoders.c ++++ b/drivers/gpu/drm/radeon/radeon_encoders.c +@@ -194,7 +194,6 @@ static void radeon_encoder_add_backlight(struct radeon_encoder *radeon_encoder, + radeon_atom_backlight_init(radeon_encoder, connector); + else + radeon_legacy_backlight_init(radeon_encoder, connector); +- rdev->mode_info.bl_encoder = radeon_encoder; + } + } + +diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +index 45715307db71..30de43366eae 100644 +--- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c ++++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +@@ -441,6 +441,7 @@ void radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, + backlight_update_status(bd); + + DRM_INFO("radeon legacy LVDS backlight initialized\n"); ++ rdev->mode_info.bl_encoder = radeon_encoder; + + return; + +diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c +index 948c33105801..91764320c56f 100644 +--- a/drivers/gpu/drm/radeon/radeon_pm.c ++++ b/drivers/gpu/drm/radeon/radeon_pm.c +@@ -720,10 +720,14 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj, + struct radeon_device *rdev = dev_get_drvdata(dev); + umode_t effective_mode = attr->mode; + +- /* Skip limit attributes if DPM is not enabled */ ++ /* Skip attributes if DPM is not enabled */ + if (rdev->pm.pm_method != PM_METHOD_DPM && + (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr || +- attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)) ++ attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr || ++ attr == &sensor_dev_attr_pwm1.dev_attr.attr || ++ attr == &sensor_dev_attr_pwm1_enable.dev_attr.attr || ++ attr == &sensor_dev_attr_pwm1_max.dev_attr.attr || ++ attr == &sensor_dev_attr_pwm1_min.dev_attr.attr)) + return 0; + + /* Skip fan attributes if fan is not present */ +@@ -1529,19 +1533,23 @@ int radeon_pm_late_init(struct radeon_device *rdev) + + if (rdev->pm.pm_method == PM_METHOD_DPM) { + if (rdev->pm.dpm_enabled) { +- ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); +- if (ret) +- DRM_ERROR("failed to create device file for dpm state\n"); +- ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); +- if (ret) +- DRM_ERROR("failed to create device file for dpm state\n"); +- /* XXX: these are noops for dpm but are here for backwards compat */ +- ret = device_create_file(rdev->dev, &dev_attr_power_profile); +- if (ret) +- DRM_ERROR("failed to create device file for power profile\n"); +- ret = device_create_file(rdev->dev, &dev_attr_power_method); +- if (ret) +- DRM_ERROR("failed to create device file for power method\n"); ++ if (!rdev->pm.sysfs_initialized) { ++ ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); ++ if (ret) ++ DRM_ERROR("failed to create device file for dpm state\n"); ++ ret = device_create_file(rdev->dev, &dev_attr_power_dpm_force_performance_level); ++ if (ret) ++ DRM_ERROR("failed to create device file for dpm state\n"); ++ /* XXX: these are noops for dpm but are here for backwards compat */ ++ ret = device_create_file(rdev->dev, &dev_attr_power_profile); ++ if (ret) ++ DRM_ERROR("failed to create device file for power profile\n"); ++ ret = device_create_file(rdev->dev, &dev_attr_power_method); ++ if (ret) ++ DRM_ERROR("failed to create device file for power method\n"); ++ if (!ret) ++ rdev->pm.sysfs_initialized = true; ++ } + + mutex_lock(&rdev->pm.mutex); + ret = radeon_dpm_late_enable(rdev); +@@ -1557,7 +1565,8 @@ int radeon_pm_late_init(struct radeon_device *rdev) + } + } + } else { +- if (rdev->pm.num_power_states > 1) { ++ if ((rdev->pm.num_power_states > 1) && ++ (!rdev->pm.sysfs_initialized)) { + /* where's the best place to put these? */ + ret = device_create_file(rdev->dev, &dev_attr_power_profile); + if (ret) +@@ -1565,6 +1574,8 @@ int radeon_pm_late_init(struct radeon_device *rdev) + ret = device_create_file(rdev->dev, &dev_attr_power_method); + if (ret) + DRM_ERROR("failed to create device file for power method\n"); ++ if (!ret) ++ rdev->pm.sysfs_initialized = true; + } + } + return ret; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +index 620bb5cf617c..15a8d7746fd2 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +@@ -1458,6 +1458,9 @@ static void __exit vmwgfx_exit(void) + drm_pci_exit(&driver, &vmw_pci_driver); + } + ++MODULE_INFO(vmw_patch, "ed7d78b2"); ++MODULE_INFO(vmw_patch, "54c12bc3"); ++ + module_init(vmwgfx_init); + module_exit(vmwgfx_exit); + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +index d26a6daa9719..d8896ed41b9e 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +@@ -636,7 +636,8 @@ extern int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, + uint32_t size, + bool shareable, + uint32_t *handle, +- struct vmw_dma_buffer **p_dma_buf); ++ struct vmw_dma_buffer **p_dma_buf, ++ struct ttm_base_object **p_base); + extern int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, + struct vmw_dma_buffer *dma_buf, + uint32_t *handle); +@@ -650,7 +651,8 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo, + uint32_t cur_validate_node); + extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo); + extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, +- uint32_t id, struct vmw_dma_buffer **out); ++ uint32_t id, struct vmw_dma_buffer **out, ++ struct ttm_base_object **base); + extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +index 97ad3bcb99a7..aee1c6ccc52d 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +@@ -887,7 +887,8 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv, + struct vmw_relocation *reloc; + int ret; + +- ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); ++ ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo, ++ NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("Could not find or use MOB buffer.\n"); + ret = -EINVAL; +@@ -949,7 +950,8 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, + struct vmw_relocation *reloc; + int ret; + +- ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo); ++ ret = vmw_user_dmabuf_lookup(sw_context->fp->tfile, handle, &vmw_bo, ++ NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("Could not find or use GMR region.\n"); + ret = -EINVAL; +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +index 87e39f68e9d0..e1898982b44a 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +@@ -484,7 +484,7 @@ int vmw_overlay_ioctl(struct drm_device *dev, void *data, + goto out_unlock; + } + +- ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf); ++ ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf, NULL); + if (ret) + goto out_unlock; + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +index 210ef15b1d09..c5b4c47e86d6 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +@@ -356,7 +356,7 @@ int vmw_user_lookup_handle(struct vmw_private *dev_priv, + } + + *out_surf = NULL; +- ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf); ++ ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf, NULL); + return ret; + } + +@@ -483,7 +483,8 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, + uint32_t size, + bool shareable, + uint32_t *handle, +- struct vmw_dma_buffer **p_dma_buf) ++ struct vmw_dma_buffer **p_dma_buf, ++ struct ttm_base_object **p_base) + { + struct vmw_user_dma_buffer *user_bo; + struct ttm_buffer_object *tmp; +@@ -517,6 +518,10 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, + } + + *p_dma_buf = &user_bo->dma; ++ if (p_base) { ++ *p_base = &user_bo->prime.base; ++ kref_get(&(*p_base)->refcount); ++ } + *handle = user_bo->prime.base.hash.key; + + out_no_base_object: +@@ -633,6 +638,7 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, + struct vmw_dma_buffer *dma_buf; + struct vmw_user_dma_buffer *user_bo; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; ++ struct ttm_base_object *buffer_base; + int ret; + + if ((arg->flags & (drm_vmw_synccpu_read | drm_vmw_synccpu_write)) == 0 +@@ -645,7 +651,8 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, + + switch (arg->op) { + case drm_vmw_synccpu_grab: +- ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf); ++ ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &dma_buf, ++ &buffer_base); + if (unlikely(ret != 0)) + return ret; + +@@ -653,6 +660,7 @@ int vmw_user_dmabuf_synccpu_ioctl(struct drm_device *dev, void *data, + dma); + ret = vmw_user_dmabuf_synccpu_grab(user_bo, tfile, arg->flags); + vmw_dmabuf_unreference(&dma_buf); ++ ttm_base_object_unref(&buffer_base); + if (unlikely(ret != 0 && ret != -ERESTARTSYS && + ret != -EBUSY)) { + DRM_ERROR("Failed synccpu grab on handle 0x%08x.\n", +@@ -694,7 +702,8 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, + return ret; + + ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, +- req->size, false, &handle, &dma_buf); ++ req->size, false, &handle, &dma_buf, ++ NULL); + if (unlikely(ret != 0)) + goto out_no_dmabuf; + +@@ -723,7 +732,8 @@ int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, + } + + int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, +- uint32_t handle, struct vmw_dma_buffer **out) ++ uint32_t handle, struct vmw_dma_buffer **out, ++ struct ttm_base_object **p_base) + { + struct vmw_user_dma_buffer *vmw_user_bo; + struct ttm_base_object *base; +@@ -745,7 +755,10 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, + vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, + prime.base); + (void)ttm_bo_reference(&vmw_user_bo->dma.base); +- ttm_base_object_unref(&base); ++ if (p_base) ++ *p_base = base; ++ else ++ ttm_base_object_unref(&base); + *out = &vmw_user_bo->dma; + + return 0; +@@ -1006,7 +1019,7 @@ int vmw_dumb_create(struct drm_file *file_priv, + + ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, + args->size, false, &args->handle, +- &dma_buf); ++ &dma_buf, NULL); + if (unlikely(ret != 0)) + goto out_no_dmabuf; + +@@ -1034,7 +1047,7 @@ int vmw_dumb_map_offset(struct drm_file *file_priv, + struct vmw_dma_buffer *out_buf; + int ret; + +- ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf); ++ ret = vmw_user_dmabuf_lookup(tfile, handle, &out_buf, NULL); + if (ret != 0) + return -EINVAL; + +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +index 6a4584a43aa6..d2751ada19b1 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +@@ -470,7 +470,7 @@ int vmw_shader_define_ioctl(struct drm_device *dev, void *data, + + if (arg->buffer_handle != SVGA3D_INVALID_ID) { + ret = vmw_user_dmabuf_lookup(tfile, arg->buffer_handle, +- &buffer); ++ &buffer, NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("Could not find buffer for shader " + "creation.\n"); +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +index 4ecdbf3e59da..17a4107639b2 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +@@ -43,6 +43,7 @@ struct vmw_user_surface { + struct vmw_surface srf; + uint32_t size; + struct drm_master *master; ++ struct ttm_base_object *backup_base; + }; + + /** +@@ -652,6 +653,8 @@ static void vmw_user_surface_base_release(struct ttm_base_object **p_base) + struct vmw_resource *res = &user_srf->srf.res; + + *p_base = NULL; ++ if (user_srf->backup_base) ++ ttm_base_object_unref(&user_srf->backup_base); + vmw_resource_unreference(&res); + } + +@@ -846,7 +849,8 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, + res->backup_size, + true, + &backup_handle, +- &res->backup); ++ &res->backup, ++ &user_srf->backup_base); + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); + goto out_unlock; +@@ -1309,7 +1313,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, + + if (req->buffer_handle != SVGA3D_INVALID_ID) { + ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle, +- &res->backup); ++ &res->backup, ++ &user_srf->backup_base); + } else if (req->drm_surface_flags & + drm_vmw_surface_flag_create_buffer) + ret = vmw_user_dmabuf_alloc(dev_priv, tfile, +@@ -1317,7 +1322,8 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, + req->drm_surface_flags & + drm_vmw_surface_flag_shareable, + &backup_handle, +- &res->backup); ++ &res->backup, ++ &user_srf->backup_base); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&res); +diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c +index 30059c1df2a3..5801227b97ab 100644 +--- a/drivers/i2c/busses/i2c-mv64xxx.c ++++ b/drivers/i2c/busses/i2c-mv64xxx.c +@@ -669,8 +669,6 @@ mv64xxx_i2c_can_offload(struct mv64xxx_i2c_data *drv_data) + struct i2c_msg *msgs = drv_data->msgs; + int num = drv_data->num_msgs; + +- return false; +- + if (!drv_data->offload_enabled) + return false; + +diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c +index 4002e6410444..c472477f9a7d 100644 +--- a/drivers/iio/accel/st_accel_core.c ++++ b/drivers/iio/accel/st_accel_core.c +@@ -149,8 +149,6 @@ + #define ST_ACCEL_4_BDU_MASK 0x40 + #define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21 + #define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04 +-#define ST_ACCEL_4_IG1_EN_ADDR 0x21 +-#define ST_ACCEL_4_IG1_EN_MASK 0x08 + #define ST_ACCEL_4_MULTIREAD_BIT true + + /* CUSTOM VALUES FOR SENSOR 5 */ +@@ -484,10 +482,6 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { + .drdy_irq = { + .addr = ST_ACCEL_4_DRDY_IRQ_ADDR, + .mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK, +- .ig1 = { +- .en_addr = ST_ACCEL_4_IG1_EN_ADDR, +- .en_mask = ST_ACCEL_4_IG1_EN_MASK, +- }, + }, + .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, + .bootime = 2, /* guess */ +diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c +index 3a972ebf3c0d..8be73524aabd 100644 +--- a/drivers/infiniband/core/cm.c ++++ b/drivers/infiniband/core/cm.c +@@ -873,6 +873,11 @@ retest: + case IB_CM_SIDR_REQ_RCVD: + spin_unlock_irq(&cm_id_priv->lock); + cm_reject_sidr_req(cm_id_priv, IB_SIDR_REJECT); ++ spin_lock_irq(&cm.lock); ++ if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) ++ rb_erase(&cm_id_priv->sidr_id_node, ++ &cm.remote_sidr_table); ++ spin_unlock_irq(&cm.lock); + break; + case IB_CM_REQ_SENT: + case IB_CM_MRA_REQ_RCVD: +@@ -3112,7 +3117,10 @@ int ib_send_cm_sidr_rep(struct ib_cm_id *cm_id, + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + spin_lock_irqsave(&cm.lock, flags); +- rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); ++ if (!RB_EMPTY_NODE(&cm_id_priv->sidr_id_node)) { ++ rb_erase(&cm_id_priv->sidr_id_node, &cm.remote_sidr_table); ++ RB_CLEAR_NODE(&cm_id_priv->sidr_id_node); ++ } + spin_unlock_irqrestore(&cm.lock, flags); + return 0; + +diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c +index 4d246861d692..41e6cb501e6a 100644 +--- a/drivers/input/mouse/alps.c ++++ b/drivers/input/mouse/alps.c +@@ -100,7 +100,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { + #define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */ + #define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with + 6-byte ALPS packet */ +-#define ALPS_DELL 0x100 /* device is a Dell laptop */ ++#define ALPS_STICK_BITS 0x100 /* separate stick button bits */ + #define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ + + static const struct alps_model_info alps_model_data[] = { +@@ -159,6 +159,43 @@ static const struct alps_protocol_info alps_v8_protocol_data = { + ALPS_PROTO_V8, 0x18, 0x18, 0 + }; + ++/* ++ * Some v2 models report the stick buttons in separate bits ++ */ ++static const struct dmi_system_id alps_dmi_has_separate_stick_buttons[] = { ++#if defined(CONFIG_DMI) && defined(CONFIG_X86) ++ { ++ /* Extrapolated from other entries */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D420"), ++ }, ++ }, ++ { ++ /* Reported-by: Hans de Bruin */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D430"), ++ }, ++ }, ++ { ++ /* Reported-by: Hans de Goede */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D620"), ++ }, ++ }, ++ { ++ /* Extrapolated from other entries */ ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), ++ DMI_MATCH(DMI_PRODUCT_NAME, "Latitude D630"), ++ }, ++ }, ++#endif ++ { } ++}; ++ + static void alps_set_abs_params_st(struct alps_data *priv, + struct input_dev *dev1); + static void alps_set_abs_params_semi_mt(struct alps_data *priv, +@@ -253,9 +290,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) + return; + } + +- /* Dell non interleaved V2 dualpoint has separate stick button bits */ +- if (priv->proto_version == ALPS_PROTO_V2 && +- priv->flags == (ALPS_DELL | ALPS_PASS | ALPS_DUALPOINT)) { ++ /* Some models have separate stick button bits */ ++ if (priv->flags & ALPS_STICK_BITS) { + left |= packet[0] & 1; + right |= packet[0] & 2; + middle |= packet[0] & 4; +@@ -2552,8 +2588,6 @@ static int alps_set_protocol(struct psmouse *psmouse, + priv->byte0 = protocol->byte0; + priv->mask0 = protocol->mask0; + priv->flags = protocol->flags; +- if (dmi_name_in_vendors("Dell")) +- priv->flags |= ALPS_DELL; + + priv->x_max = 2000; + priv->y_max = 1400; +@@ -2568,6 +2602,8 @@ static int alps_set_protocol(struct psmouse *psmouse, + priv->set_abs_params = alps_set_abs_params_st; + priv->x_max = 1023; + priv->y_max = 767; ++ if (dmi_check_system(alps_dmi_has_separate_stick_buttons)) ++ priv->flags |= ALPS_STICK_BITS; + break; + + case ALPS_PROTO_V3: +diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c +index 658ee39e6569..1b10e5fd6ef6 100644 +--- a/drivers/iommu/amd_iommu.c ++++ b/drivers/iommu/amd_iommu.c +@@ -1974,8 +1974,8 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats) + static void clear_dte_entry(u16 devid) + { + /* remove entry from the device table seen by the hardware */ +- amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV; +- amd_iommu_dev_table[devid].data[1] = 0; ++ amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV; ++ amd_iommu_dev_table[devid].data[1] &= DTE_FLAG_MASK; + + amd_iommu_apply_erratum_63(devid); + } +diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h +index f65908841be0..c9b64722f623 100644 +--- a/drivers/iommu/amd_iommu_types.h ++++ b/drivers/iommu/amd_iommu_types.h +@@ -295,6 +295,7 @@ + #define IOMMU_PTE_IR (1ULL << 61) + #define IOMMU_PTE_IW (1ULL << 62) + ++#define DTE_FLAG_MASK (0x3ffULL << 32) + #define DTE_FLAG_IOTLB (0x01UL << 32) + #define DTE_FLAG_GV (0x01ULL << 55) + #define DTE_GLX_SHIFT (56) +diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c +index f7b875bb70d4..c3b8a5b9f035 100644 +--- a/drivers/iommu/amd_iommu_v2.c ++++ b/drivers/iommu/amd_iommu_v2.c +@@ -516,6 +516,13 @@ static void do_fault(struct work_struct *work) + goto out; + } + ++ if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) { ++ /* handle_mm_fault would BUG_ON() */ ++ up_read(&mm->mmap_sem); ++ handle_fault_error(fault); ++ goto out; ++ } ++ + ret = handle_mm_fault(mm, vma, address, write); + if (ret & VM_FAULT_ERROR) { + /* failed to service fault */ +diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c +index 7553cb90627f..bd1b8ad8af44 100644 +--- a/drivers/iommu/intel-iommu.c ++++ b/drivers/iommu/intel-iommu.c +@@ -2109,15 +2109,19 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, + return -ENOMEM; + /* It is large page*/ + if (largepage_lvl > 1) { ++ unsigned long nr_superpages, end_pfn; ++ + pteval |= DMA_PTE_LARGE_PAGE; + lvl_pages = lvl_to_nr_pages(largepage_lvl); ++ ++ nr_superpages = sg_res / lvl_pages; ++ end_pfn = iov_pfn + nr_superpages * lvl_pages - 1; ++ + /* + * Ensure that old small page tables are +- * removed to make room for superpage, +- * if they exist. ++ * removed to make room for superpage(s). + */ +- dma_pte_free_pagetable(domain, iov_pfn, +- iov_pfn + lvl_pages - 1); ++ dma_pte_free_pagetable(domain, iov_pfn, end_pfn); + } else { + pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; + } +diff --git a/drivers/irqchip/irq-tegra.c b/drivers/irqchip/irq-tegra.c +index f67bbd80433e..ab5353a96a82 100644 +--- a/drivers/irqchip/irq-tegra.c ++++ b/drivers/irqchip/irq-tegra.c +@@ -215,6 +215,7 @@ static struct irq_chip tegra_ictlr_chip = { + .irq_unmask = tegra_unmask, + .irq_retrigger = tegra_retrigger, + .irq_set_wake = tegra_set_wake, ++ .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND, + #ifdef CONFIG_SMP + .irq_set_affinity = irq_chip_set_affinity_parent, +diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c +index 20cc36b01b77..0a17d1b91a81 100644 +--- a/drivers/md/dm-cache-metadata.c ++++ b/drivers/md/dm-cache-metadata.c +@@ -634,10 +634,10 @@ static int __commit_transaction(struct dm_cache_metadata *cmd, + + disk_super = dm_block_data(sblock); + ++ disk_super->flags = cpu_to_le32(cmd->flags); + if (mutator) + update_flags(disk_super, mutator); + +- disk_super->flags = cpu_to_le32(cmd->flags); + disk_super->mapping_root = cpu_to_le64(cmd->root); + disk_super->hint_root = cpu_to_le64(cmd->hint_root); + disk_super->discard_root = cpu_to_le64(cmd->discard_root); +diff --git a/drivers/md/md.c b/drivers/md/md.c +index e25f00f0138a..95e7b72a164a 100644 +--- a/drivers/md/md.c ++++ b/drivers/md/md.c +@@ -8030,8 +8030,7 @@ static int remove_and_add_spares(struct mddev *mddev, + !test_bit(Bitmap_sync, &rdev->flags))) + continue; + +- if (rdev->saved_raid_disk < 0) +- rdev->recovery_offset = 0; ++ rdev->recovery_offset = 0; + if (mddev->pers-> + hot_add_disk(mddev, rdev) == 0) { + if (sysfs_link_rdev(mddev, rdev)) +diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c +index 4222f774cf36..1dac15d1697c 100644 +--- a/drivers/md/persistent-data/dm-btree-remove.c ++++ b/drivers/md/persistent-data/dm-btree-remove.c +@@ -301,11 +301,16 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, + { + int s; + uint32_t max_entries = le32_to_cpu(left->header.max_entries); +- unsigned target = (nr_left + nr_center + nr_right) / 3; +- BUG_ON(target > max_entries); ++ unsigned total = nr_left + nr_center + nr_right; ++ unsigned target_right = total / 3; ++ unsigned remainder = (target_right * 3) != total; ++ unsigned target_left = target_right + remainder; ++ ++ BUG_ON(target_left > max_entries); ++ BUG_ON(target_right > max_entries); + + if (nr_left < nr_right) { +- s = nr_left - target; ++ s = nr_left - target_left; + + if (s < 0 && nr_center < -s) { + /* not enough in central node */ +@@ -316,10 +321,10 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, + } else + shift(left, center, s); + +- shift(center, right, target - nr_right); ++ shift(center, right, target_right - nr_right); + + } else { +- s = target - nr_right; ++ s = target_right - nr_right; + if (s > 0 && nr_center < s) { + /* not enough in central node */ + shift(center, right, nr_center); +@@ -329,7 +334,7 @@ static void redistribute3(struct dm_btree_info *info, struct btree_node *parent, + } else + shift(center, right, s); + +- shift(left, center, nr_left - target); ++ shift(left, center, nr_left - target_left); + } + + *key_ptr(parent, c->index) = center->keys[0]; +diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c +index c7726cebc495..d6e47033b5e0 100644 +--- a/drivers/md/persistent-data/dm-btree.c ++++ b/drivers/md/persistent-data/dm-btree.c +@@ -523,7 +523,7 @@ static int btree_split_beneath(struct shadow_spine *s, uint64_t key) + + r = new_block(s->info, &right); + if (r < 0) { +- /* FIXME: put left */ ++ unlock_block(s->info, left); + return r; + } + +diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c +index 967a4ed73929..d10d3008227e 100644 +--- a/drivers/md/raid1.c ++++ b/drivers/md/raid1.c +@@ -2249,7 +2249,7 @@ static int narrow_write_error(struct r1bio *r1_bio, int i) + bio_trim(wbio, sector - r1_bio->sector, sectors); + wbio->bi_iter.bi_sector += rdev->data_offset; + wbio->bi_bdev = rdev->bdev; +- if (submit_bio_wait(WRITE, wbio) == 0) ++ if (submit_bio_wait(WRITE, wbio) < 0) + /* failure! */ + ok = rdev_set_badblocks(rdev, sector, + sectors, 0) +diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c +index 38c58e19cfce..d4b70d90de9c 100644 +--- a/drivers/md/raid10.c ++++ b/drivers/md/raid10.c +@@ -2580,7 +2580,7 @@ static int narrow_write_error(struct r10bio *r10_bio, int i) + choose_data_offset(r10_bio, rdev) + + (sector - r10_bio->sector)); + wbio->bi_bdev = rdev->bdev; +- if (submit_bio_wait(WRITE, wbio) == 0) ++ if (submit_bio_wait(WRITE, wbio) < 0) + /* Failure! */ + ok = rdev_set_badblocks(rdev, sector, + sectors, 0) +diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c +index f757023fc458..0d4f7b1b7f73 100644 +--- a/drivers/md/raid5.c ++++ b/drivers/md/raid5.c +@@ -3505,6 +3505,7 @@ returnbi: + } + if (!discard_pending && + test_bit(R5_Discard, &sh->dev[sh->pd_idx].flags)) { ++ int hash; + clear_bit(R5_Discard, &sh->dev[sh->pd_idx].flags); + clear_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags); + if (sh->qd_idx >= 0) { +@@ -3518,16 +3519,17 @@ returnbi: + * no updated data, so remove it from hash list and the stripe + * will be reinitialized + */ +- spin_lock_irq(&conf->device_lock); + unhash: ++ hash = sh->hash_lock_index; ++ spin_lock_irq(conf->hash_locks + hash); + remove_hash(sh); ++ spin_unlock_irq(conf->hash_locks + hash); + if (head_sh->batch_head) { + sh = list_first_entry(&sh->batch_list, + struct stripe_head, batch_list); + if (sh != head_sh) + goto unhash; + } +- spin_unlock_irq(&conf->device_lock); + sh = head_sh; + + if (test_bit(STRIPE_SYNC_REQUESTED, &sh->state)) +diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c +index e9b2d2b69b1d..377fb6991ab3 100644 +--- a/drivers/media/dvb-frontends/m88ds3103.c ++++ b/drivers/media/dvb-frontends/m88ds3103.c +@@ -18,6 +18,27 @@ + + static struct dvb_frontend_ops m88ds3103_ops; + ++/* write single register with mask */ ++static int m88ds3103_update_bits(struct m88ds3103_dev *dev, ++ u8 reg, u8 mask, u8 val) ++{ ++ int ret; ++ u8 tmp; ++ ++ /* no need for read if whole reg is written */ ++ if (mask != 0xff) { ++ ret = regmap_bulk_read(dev->regmap, reg, &tmp, 1); ++ if (ret) ++ return ret; ++ ++ val &= mask; ++ tmp &= ~mask; ++ val |= tmp; ++ } ++ ++ return regmap_bulk_write(dev->regmap, reg, &val, 1); ++} ++ + /* write reg val table using reg addr auto increment */ + static int m88ds3103_wr_reg_val_tab(struct m88ds3103_dev *dev, + const struct m88ds3103_reg_val *tab, int tab_len) +@@ -394,10 +415,10 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) + u8tmp2 = 0x00; /* 0b00 */ + break; + } +- ret = regmap_update_bits(dev->regmap, 0x22, 0xc0, u8tmp1 << 6); ++ ret = m88ds3103_update_bits(dev, 0x22, 0xc0, u8tmp1 << 6); + if (ret) + goto err; +- ret = regmap_update_bits(dev->regmap, 0x24, 0xc0, u8tmp2 << 6); ++ ret = m88ds3103_update_bits(dev, 0x24, 0xc0, u8tmp2 << 6); + if (ret) + goto err; + } +@@ -455,13 +476,13 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) + if (ret) + goto err; + } +- ret = regmap_update_bits(dev->regmap, 0x9d, 0x08, 0x08); ++ ret = m88ds3103_update_bits(dev, 0x9d, 0x08, 0x08); + if (ret) + goto err; + ret = regmap_write(dev->regmap, 0xf1, 0x01); + if (ret) + goto err; +- ret = regmap_update_bits(dev->regmap, 0x30, 0x80, 0x80); ++ ret = m88ds3103_update_bits(dev, 0x30, 0x80, 0x80); + if (ret) + goto err; + } +@@ -498,7 +519,7 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) + switch (dev->cfg->ts_mode) { + case M88DS3103_TS_SERIAL: + case M88DS3103_TS_SERIAL_D7: +- ret = regmap_update_bits(dev->regmap, 0x29, 0x20, u8tmp1); ++ ret = m88ds3103_update_bits(dev, 0x29, 0x20, u8tmp1); + if (ret) + goto err; + u8tmp1 = 0; +@@ -567,11 +588,11 @@ static int m88ds3103_set_frontend(struct dvb_frontend *fe) + if (ret) + goto err; + +- ret = regmap_update_bits(dev->regmap, 0x4d, 0x02, dev->cfg->spec_inv << 1); ++ ret = m88ds3103_update_bits(dev, 0x4d, 0x02, dev->cfg->spec_inv << 1); + if (ret) + goto err; + +- ret = regmap_update_bits(dev->regmap, 0x30, 0x10, dev->cfg->agc_inv << 4); ++ ret = m88ds3103_update_bits(dev, 0x30, 0x10, dev->cfg->agc_inv << 4); + if (ret) + goto err; + +@@ -625,13 +646,13 @@ static int m88ds3103_init(struct dvb_frontend *fe) + dev->warm = false; + + /* wake up device from sleep */ +- ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x01); ++ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x01); + if (ret) + goto err; +- ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x00); ++ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x00); + if (ret) + goto err; +- ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x00); ++ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x00); + if (ret) + goto err; + +@@ -749,18 +770,18 @@ static int m88ds3103_sleep(struct dvb_frontend *fe) + utmp = 0x29; + else + utmp = 0x27; +- ret = regmap_update_bits(dev->regmap, utmp, 0x01, 0x00); ++ ret = m88ds3103_update_bits(dev, utmp, 0x01, 0x00); + if (ret) + goto err; + + /* sleep */ +- ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00); ++ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); + if (ret) + goto err; +- ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01); ++ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); + if (ret) + goto err; +- ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x10); ++ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); + if (ret) + goto err; + +@@ -992,12 +1013,12 @@ static int m88ds3103_set_tone(struct dvb_frontend *fe, + } + + utmp = tone << 7 | dev->cfg->envelope_mode << 5; +- ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); ++ ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); + if (ret) + goto err; + + utmp = 1 << 2; +- ret = regmap_update_bits(dev->regmap, 0xa1, reg_a1_mask, utmp); ++ ret = m88ds3103_update_bits(dev, 0xa1, reg_a1_mask, utmp); + if (ret) + goto err; + +@@ -1047,7 +1068,7 @@ static int m88ds3103_set_voltage(struct dvb_frontend *fe, + voltage_dis ^= dev->cfg->lnb_en_pol; + + utmp = voltage_dis << 1 | voltage_sel << 0; +- ret = regmap_update_bits(dev->regmap, 0xa2, 0x03, utmp); ++ ret = m88ds3103_update_bits(dev, 0xa2, 0x03, utmp); + if (ret) + goto err; + +@@ -1080,7 +1101,7 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, + } + + utmp = dev->cfg->envelope_mode << 5; +- ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); ++ ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); + if (ret) + goto err; + +@@ -1115,12 +1136,12 @@ static int m88ds3103_diseqc_send_master_cmd(struct dvb_frontend *fe, + } else { + dev_dbg(&client->dev, "diseqc tx timeout\n"); + +- ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40); ++ ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); + if (ret) + goto err; + } + +- ret = regmap_update_bits(dev->regmap, 0xa2, 0xc0, 0x80); ++ ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); + if (ret) + goto err; + +@@ -1152,7 +1173,7 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, + } + + utmp = dev->cfg->envelope_mode << 5; +- ret = regmap_update_bits(dev->regmap, 0xa2, 0xe0, utmp); ++ ret = m88ds3103_update_bits(dev, 0xa2, 0xe0, utmp); + if (ret) + goto err; + +@@ -1194,12 +1215,12 @@ static int m88ds3103_diseqc_send_burst(struct dvb_frontend *fe, + } else { + dev_dbg(&client->dev, "diseqc tx timeout\n"); + +- ret = regmap_update_bits(dev->regmap, 0xa1, 0xc0, 0x40); ++ ret = m88ds3103_update_bits(dev, 0xa1, 0xc0, 0x40); + if (ret) + goto err; + } + +- ret = regmap_update_bits(dev->regmap, 0xa2, 0xc0, 0x80); ++ ret = m88ds3103_update_bits(dev, 0xa2, 0xc0, 0x80); + if (ret) + goto err; + +@@ -1435,13 +1456,13 @@ static int m88ds3103_probe(struct i2c_client *client, + goto err_kfree; + + /* sleep */ +- ret = regmap_update_bits(dev->regmap, 0x08, 0x01, 0x00); ++ ret = m88ds3103_update_bits(dev, 0x08, 0x01, 0x00); + if (ret) + goto err_kfree; +- ret = regmap_update_bits(dev->regmap, 0x04, 0x01, 0x01); ++ ret = m88ds3103_update_bits(dev, 0x04, 0x01, 0x01); + if (ret) + goto err_kfree; +- ret = regmap_update_bits(dev->regmap, 0x23, 0x10, 0x10); ++ ret = m88ds3103_update_bits(dev, 0x23, 0x10, 0x10); + if (ret) + goto err_kfree; + +diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c +index 25e238c370e5..cb6a49b8c1ce 100644 +--- a/drivers/media/dvb-frontends/si2168.c ++++ b/drivers/media/dvb-frontends/si2168.c +@@ -502,6 +502,10 @@ static int si2168_init(struct dvb_frontend *fe) + /* firmware is in the new format */ + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; ++ if (len > SI2168_ARGLEN) { ++ ret = -EINVAL; ++ break; ++ } + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + cmd.wlen = len; + cmd.rlen = 1; +diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c +index a6245ef379c4..416c865eb595 100644 +--- a/drivers/media/tuners/si2157.c ++++ b/drivers/media/tuners/si2157.c +@@ -166,6 +166,10 @@ static int si2157_init(struct dvb_frontend *fe) + + for (remaining = fw->size; remaining > 0; remaining -= 17) { + len = fw->data[fw->size - remaining]; ++ if (len > SI2157_ARGLEN) { ++ dev_err(&client->dev, "Bad firmware length\n"); ++ goto err_release_firmware; ++ } + memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); + cmd.wlen = len; + cmd.rlen = 1; +diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +index c3cac4c12fb3..197a4f2e54d2 100644 +--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c ++++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +@@ -34,6 +34,14 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) + unsigned int pipe; + u8 requesttype; + ++ mutex_lock(&d->usb_mutex); ++ ++ if (req->size > sizeof(dev->buf)) { ++ dev_err(&d->intf->dev, "too large message %u\n", req->size); ++ ret = -EINVAL; ++ goto err_mutex_unlock; ++ } ++ + if (req->index & CMD_WR_FLAG) { + /* write */ + memcpy(dev->buf, req->data, req->size); +@@ -50,14 +58,17 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) + dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value, + req->index, dev->buf, req->size); + if (ret < 0) +- goto err; ++ goto err_mutex_unlock; + + /* read request, copy returned data to return buf */ + if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, dev->buf, req->size); + ++ mutex_unlock(&d->usb_mutex); ++ + return 0; +-err: ++err_mutex_unlock: ++ mutex_unlock(&d->usb_mutex); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); + return ret; + } +diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +index 9f6115a2ee01..138062960a73 100644 +--- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h ++++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +@@ -71,7 +71,7 @@ + + + struct rtl28xxu_dev { +- u8 buf[28]; ++ u8 buf[128]; + u8 chip_id; + u8 tuner; + char *tuner_name; +diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c +index b78cf5d403a3..7fc9174d4619 100644 +--- a/drivers/mmc/card/mmc_test.c ++++ b/drivers/mmc/card/mmc_test.c +@@ -2263,15 +2263,12 @@ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test) + /* + * eMMC hardware reset. + */ +-static int mmc_test_hw_reset(struct mmc_test_card *test) ++static int mmc_test_reset(struct mmc_test_card *test) + { + struct mmc_card *card = test->card; + struct mmc_host *host = card->host; + int err; + +- if (!mmc_card_mmc(card) || !mmc_can_reset(card)) +- return RESULT_UNSUP_CARD; +- + err = mmc_hw_reset(host); + if (!err) + return RESULT_OK; +@@ -2605,8 +2602,8 @@ static const struct mmc_test_case mmc_test_cases[] = { + }, + + { +- .name = "eMMC hardware reset", +- .run = mmc_test_hw_reset, ++ .name = "Reset test", ++ .run = mmc_test_reset, + }, + }; + +diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c +index e726903170a8..f6cd995dbe92 100644 +--- a/drivers/mmc/core/mmc.c ++++ b/drivers/mmc/core/mmc.c +@@ -1924,7 +1924,6 @@ EXPORT_SYMBOL(mmc_can_reset); + static int mmc_reset(struct mmc_host *host) + { + struct mmc_card *card = host->card; +- u32 status; + + if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) + return -EOPNOTSUPP; +@@ -1937,12 +1936,6 @@ static int mmc_reset(struct mmc_host *host) + + host->ops->hw_reset(host); + +- /* If the reset has happened, then a status command will fail */ +- if (!mmc_send_status(card, &status)) { +- mmc_host_clk_release(host); +- return -ENOSYS; +- } +- + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); + mmc_host_clk_release(host); +diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c +index eff0e5325e6a..bfddc9efd6cc 100644 +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -874,6 +874,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) + hw->max_rate_tries = 10; + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); ++ hw->extra_tx_headroom = 4; + + hw->wiphy->available_antennas_rx = BIT(ah->caps.max_rxchains) - 1; + hw->wiphy->available_antennas_tx = BIT(ah->caps.max_txchains) - 1; +diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c +index 1d2223df5cb0..e7d3566c714b 100644 +--- a/drivers/net/wireless/iwlwifi/dvm/lib.c ++++ b/drivers/net/wireless/iwlwifi/dvm/lib.c +@@ -1022,7 +1022,7 @@ static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); +- aes_sc->pn = cpu_to_le64( ++ aes_sc[i].pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | +diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c +index cc35f796d406..d7acbd147bd1 100644 +--- a/drivers/net/wireless/iwlwifi/iwl-7000.c ++++ b/drivers/net/wireless/iwlwifi/iwl-7000.c +@@ -348,6 +348,6 @@ const struct iwl_cfg iwl7265d_n_cfg = { + }; + + MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +-MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK)); ++MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); + MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); + MODULE_FIRMWARE(IWL7265D_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); +diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c +index 4165d104e4c3..f60b89baab7a 100644 +--- a/drivers/net/wireless/iwlwifi/mvm/d3.c ++++ b/drivers/net/wireless/iwlwifi/mvm/d3.c +@@ -274,18 +274,13 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, + break; + case WLAN_CIPHER_SUITE_CCMP: + if (sta) { +- u8 *pn = seq.ccmp.pn; ++ u64 pn64; + + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; + aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; + +- ieee80211_get_key_tx_seq(key, &seq); +- aes_tx_sc->pn = cpu_to_le64((u64)pn[5] | +- ((u64)pn[4] << 8) | +- ((u64)pn[3] << 16) | +- ((u64)pn[2] << 24) | +- ((u64)pn[1] << 32) | +- ((u64)pn[0] << 40)); ++ pn64 = atomic64_read(&key->tx_pn); ++ aes_tx_sc->pn = cpu_to_le64(pn64); + } else { + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; + } +@@ -298,12 +293,12 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); +- aes_sc->pn = cpu_to_le64((u64)pn[5] | +- ((u64)pn[4] << 8) | +- ((u64)pn[3] << 16) | +- ((u64)pn[2] << 24) | +- ((u64)pn[1] << 32) | +- ((u64)pn[0] << 40)); ++ aes_sc[i].pn = cpu_to_le64((u64)pn[5] | ++ ((u64)pn[4] << 8) | ++ ((u64)pn[3] << 16) | ++ ((u64)pn[2] << 24) | ++ ((u64)pn[1] << 32) | ++ ((u64)pn[0] << 40)); + } + data->use_rsc_tsc = true; + break; +@@ -1446,15 +1441,15 @@ static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: +- iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq); + iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); ++ atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); + iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); ++ ieee80211_set_key_tx_seq(key, &seq); + break; + } +- ieee80211_set_key_tx_seq(key, &seq); + + /* that's it for this key */ + return; +diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c +index eb10c5ee4a14..b49367e1cfd2 100644 +--- a/drivers/net/wireless/iwlwifi/mvm/fw.c ++++ b/drivers/net/wireless/iwlwifi/mvm/fw.c +@@ -364,7 +364,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) + * abort after reading the nvm in case RF Kill is on, we will complete + * the init seq later when RF kill will switch to off + */ +- if (iwl_mvm_is_radio_killed(mvm)) { ++ if (iwl_mvm_is_radio_hw_killed(mvm)) { + IWL_DEBUG_RF_KILL(mvm, + "jump over all phy activities due to RF kill\n"); + iwl_remove_notification(&mvm->notif_wait, &calib_wait); +@@ -397,7 +397,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) + ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, + MVM_UCODE_CALIB_TIMEOUT); + +- if (ret && iwl_mvm_is_radio_killed(mvm)) { ++ if (ret && iwl_mvm_is_radio_hw_killed(mvm)) { + IWL_DEBUG_RF_KILL(mvm, "RFKILL while calibrating.\n"); + ret = 1; + } +diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c +index dfdab38e2d4a..f82019c0c4c0 100644 +--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c ++++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c +@@ -2373,6 +2373,7 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, + iwl_mvm_remove_time_event(mvm, mvmvif, + &mvmvif->time_event_data); + RCU_INIT_POINTER(mvm->csa_vif, NULL); ++ mvmvif->csa_countdown = false; + } + + if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) { +diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h +index 2d4bad5fe825..4a6f1627ae43 100644 +--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h ++++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h +@@ -848,6 +848,11 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) + test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); + } + ++static inline bool iwl_mvm_is_radio_hw_killed(struct iwl_mvm *mvm) ++{ ++ return test_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); ++} ++ + /* Must be called with rcu_read_lock() held and it can only be + * released when mvmsta is not needed anymore. + */ +diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c +index e4fa50075ffd..61c2b0ad5db7 100644 +--- a/drivers/net/wireless/iwlwifi/mvm/ops.c ++++ b/drivers/net/wireless/iwlwifi/mvm/ops.c +@@ -582,6 +582,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, + ieee80211_unregister_hw(mvm->hw); + iwl_mvm_leds_exit(mvm); + out_free: ++ flush_delayed_work(&mvm->fw_dump_wk); + iwl_phy_db_free(mvm->phy_db); + kfree(mvm->scan_cmd); + if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) +diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c +index 9f65c1cff1b1..865d578dee82 100644 +--- a/drivers/net/wireless/iwlwifi/pcie/drv.c ++++ b/drivers/net/wireless/iwlwifi/pcie/drv.c +@@ -414,6 +414,11 @@ static const struct pci_device_id iwl_hw_card_ids[] = { + {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, ++ {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)}, ++ {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)}, ++ {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)}, ++ {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)}, ++ {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)}, + + /* 8000 Series */ + {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, +diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h +index d4567d12e07e..5da6703942d9 100644 +--- a/drivers/net/wireless/rtlwifi/pci.h ++++ b/drivers/net/wireless/rtlwifi/pci.h +@@ -247,6 +247,8 @@ struct rtl_pci { + /* MSI support */ + bool msi_support; + bool using_msi; ++ /* interrupt clear before set */ ++ bool int_clear; + }; + + struct mp_adapter { +diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +index b7f18e2155eb..6e9418ed90c2 100644 +--- a/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c ++++ b/drivers/net/wireless/rtlwifi/rtl8821ae/hw.c +@@ -2253,11 +2253,28 @@ void rtl8821ae_set_qos(struct ieee80211_hw *hw, int aci) + } + } + ++static void rtl8821ae_clear_interrupt(struct ieee80211_hw *hw) ++{ ++ struct rtl_priv *rtlpriv = rtl_priv(hw); ++ u32 tmp = rtl_read_dword(rtlpriv, REG_HISR); ++ ++ rtl_write_dword(rtlpriv, REG_HISR, tmp); ++ ++ tmp = rtl_read_dword(rtlpriv, REG_HISRE); ++ rtl_write_dword(rtlpriv, REG_HISRE, tmp); ++ ++ tmp = rtl_read_dword(rtlpriv, REG_HSISR); ++ rtl_write_dword(rtlpriv, REG_HSISR, tmp); ++} ++ + void rtl8821ae_enable_interrupt(struct ieee80211_hw *hw) + { + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + ++ if (!rtlpci->int_clear) ++ rtl8821ae_clear_interrupt(hw);/*clear it here first*/ ++ + rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); + rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); + rtlpci->irq_enabled = true; +diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c +index a4988121e1ab..8ee141a55bc5 100644 +--- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c ++++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c +@@ -96,6 +96,7 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) + + rtl8821ae_bt_reg_init(hw); + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; ++ rtlpci->int_clear = rtlpriv->cfg->mod_params->int_clear; + rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); + + rtlpriv->dm.dm_initialgain_enable = 1; +@@ -167,6 +168,7 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) + rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; + rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; ++ rtlpci->msi_support = rtlpriv->cfg->mod_params->int_clear; + if (rtlpriv->cfg->mod_params->disable_watchdog) + pr_info("watchdog disabled\n"); + rtlpriv->psc.reg_fwctrl_lps = 3; +@@ -308,6 +310,7 @@ static struct rtl_mod_params rtl8821ae_mod_params = { + .swctrl_lps = false, + .fwctrl_lps = true, + .msi_support = true, ++ .int_clear = true, + .debug = DBG_EMERG, + .disable_watchdog = 0, + }; +@@ -437,6 +440,7 @@ module_param_named(fwlps, rtl8821ae_mod_params.fwctrl_lps, bool, 0444); + module_param_named(msi, rtl8821ae_mod_params.msi_support, bool, 0444); + module_param_named(disable_watchdog, rtl8821ae_mod_params.disable_watchdog, + bool, 0444); ++module_param_named(int_clear, rtl8821ae_mod_params.int_clear, bool, 0444); + MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); + MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); + MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +@@ -444,6 +448,7 @@ MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); + MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 1)\n"); + MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); + MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); ++MODULE_PARM_DESC(int_clear, "Set to 1 to disable interrupt clear before set (default 0)\n"); + + static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); + +diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h +index 2b770b5e2620..0a3570aa6651 100644 +--- a/drivers/net/wireless/rtlwifi/wifi.h ++++ b/drivers/net/wireless/rtlwifi/wifi.h +@@ -2234,6 +2234,9 @@ struct rtl_mod_params { + + /* default 0: 1 means disable */ + bool disable_watchdog; ++ ++ /* default 0: 1 means do not disable interrupts */ ++ bool int_clear; + }; + + struct rtl_hal_usbint_cfg { +diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c +index 312f23a8429c..92618686604c 100644 +--- a/drivers/pci/pci-sysfs.c ++++ b/drivers/pci/pci-sysfs.c +@@ -216,7 +216,7 @@ static ssize_t numa_node_store(struct device *dev, + if (ret) + return ret; + +- if (!node_online(node)) ++ if (node >= MAX_NUMNODES || !node_online(node)) + return -EINVAL; + + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); +diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c +index 2062c224e32f..b2602210784d 100644 +--- a/drivers/pinctrl/intel/pinctrl-baytrail.c ++++ b/drivers/pinctrl/intel/pinctrl-baytrail.c +@@ -146,7 +146,7 @@ struct byt_gpio_pin_context { + struct byt_gpio { + struct gpio_chip chip; + struct platform_device *pdev; +- spinlock_t lock; ++ raw_spinlock_t lock; + void __iomem *reg_base; + struct pinctrl_gpio_range *range; + struct byt_gpio_pin_context *saved_context; +@@ -174,11 +174,11 @@ static void byt_gpio_clear_triggering(struct byt_gpio *vg, unsigned offset) + unsigned long flags; + u32 value; + +- spin_lock_irqsave(&vg->lock, flags); ++ raw_spin_lock_irqsave(&vg->lock, flags); + value = readl(reg); + value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); + writel(value, reg); +- spin_unlock_irqrestore(&vg->lock, flags); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + } + + static u32 byt_get_gpio_mux(struct byt_gpio *vg, unsigned offset) +@@ -201,6 +201,9 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) + struct byt_gpio *vg = to_byt_gpio(chip); + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_CONF0_REG); + u32 value, gpio_mux; ++ unsigned long flags; ++ ++ raw_spin_lock_irqsave(&vg->lock, flags); + + /* + * In most cases, func pin mux 000 means GPIO function. +@@ -214,18 +217,16 @@ static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) + value = readl(reg) & BYT_PIN_MUX; + gpio_mux = byt_get_gpio_mux(vg, offset); + if (WARN_ON(gpio_mux != value)) { +- unsigned long flags; +- +- spin_lock_irqsave(&vg->lock, flags); + value = readl(reg) & ~BYT_PIN_MUX; + value |= gpio_mux; + writel(value, reg); +- spin_unlock_irqrestore(&vg->lock, flags); + + dev_warn(&vg->pdev->dev, + "pin %u forcibly re-configured as GPIO\n", offset); + } + ++ raw_spin_unlock_irqrestore(&vg->lock, flags); ++ + pm_runtime_get(&vg->pdev->dev); + + return 0; +@@ -250,7 +251,7 @@ static int byt_irq_type(struct irq_data *d, unsigned type) + if (offset >= vg->chip.ngpio) + return -EINVAL; + +- spin_lock_irqsave(&vg->lock, flags); ++ raw_spin_lock_irqsave(&vg->lock, flags); + value = readl(reg); + + WARN(value & BYT_DIRECT_IRQ_EN, +@@ -269,7 +270,7 @@ static int byt_irq_type(struct irq_data *d, unsigned type) + else if (type & IRQ_TYPE_LEVEL_MASK) + __irq_set_handler_locked(d->irq, handle_level_irq); + +- spin_unlock_irqrestore(&vg->lock, flags); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + + return 0; + } +@@ -277,7 +278,15 @@ static int byt_irq_type(struct irq_data *d, unsigned type) + static int byt_gpio_get(struct gpio_chip *chip, unsigned offset) + { + void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); +- return readl(reg) & BYT_LEVEL; ++ struct byt_gpio *vg = to_byt_gpio(chip); ++ unsigned long flags; ++ u32 val; ++ ++ raw_spin_lock_irqsave(&vg->lock, flags); ++ val = readl(reg); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); ++ ++ return val & BYT_LEVEL; + } + + static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +@@ -287,7 +296,7 @@ static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + unsigned long flags; + u32 old_val; + +- spin_lock_irqsave(&vg->lock, flags); ++ raw_spin_lock_irqsave(&vg->lock, flags); + + old_val = readl(reg); + +@@ -296,7 +305,7 @@ static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) + else + writel(old_val & ~BYT_LEVEL, reg); + +- spin_unlock_irqrestore(&vg->lock, flags); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + } + + static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +@@ -306,13 +315,13 @@ static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) + unsigned long flags; + u32 value; + +- spin_lock_irqsave(&vg->lock, flags); ++ raw_spin_lock_irqsave(&vg->lock, flags); + + value = readl(reg) | BYT_DIR_MASK; + value &= ~BYT_INPUT_EN; /* active low */ + writel(value, reg); + +- spin_unlock_irqrestore(&vg->lock, flags); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + + return 0; + } +@@ -326,7 +335,7 @@ static int byt_gpio_direction_output(struct gpio_chip *chip, + unsigned long flags; + u32 reg_val; + +- spin_lock_irqsave(&vg->lock, flags); ++ raw_spin_lock_irqsave(&vg->lock, flags); + + /* + * Before making any direction modifications, do a check if gpio +@@ -345,7 +354,7 @@ static int byt_gpio_direction_output(struct gpio_chip *chip, + else + writel(reg_val & ~BYT_LEVEL, reg); + +- spin_unlock_irqrestore(&vg->lock, flags); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + + return 0; + } +@@ -354,18 +363,19 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) + { + struct byt_gpio *vg = to_byt_gpio(chip); + int i; +- unsigned long flags; + u32 conf0, val, offs; + +- spin_lock_irqsave(&vg->lock, flags); +- + for (i = 0; i < vg->chip.ngpio; i++) { + const char *pull_str = NULL; + const char *pull = NULL; ++ unsigned long flags; + const char *label; + offs = vg->range->pins[i] * 16; ++ ++ raw_spin_lock_irqsave(&vg->lock, flags); + conf0 = readl(vg->reg_base + offs + BYT_CONF0_REG); + val = readl(vg->reg_base + offs + BYT_VAL_REG); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + + label = gpiochip_is_requested(chip, i); + if (!label) +@@ -418,7 +428,6 @@ static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) + + seq_puts(s, "\n"); + } +- spin_unlock_irqrestore(&vg->lock, flags); + } + + static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +@@ -450,8 +459,10 @@ static void byt_irq_ack(struct irq_data *d) + unsigned offset = irqd_to_hwirq(d); + void __iomem *reg; + ++ raw_spin_lock(&vg->lock); + reg = byt_gpio_reg(&vg->chip, offset, BYT_INT_STAT_REG); + writel(BIT(offset % 32), reg); ++ raw_spin_unlock(&vg->lock); + } + + static void byt_irq_unmask(struct irq_data *d) +@@ -463,9 +474,9 @@ static void byt_irq_unmask(struct irq_data *d) + void __iomem *reg; + u32 value; + +- spin_lock_irqsave(&vg->lock, flags); +- + reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); ++ ++ raw_spin_lock_irqsave(&vg->lock, flags); + value = readl(reg); + + switch (irqd_get_trigger_type(d)) { +@@ -486,7 +497,7 @@ static void byt_irq_unmask(struct irq_data *d) + + writel(value, reg); + +- spin_unlock_irqrestore(&vg->lock, flags); ++ raw_spin_unlock_irqrestore(&vg->lock, flags); + } + + static void byt_irq_mask(struct irq_data *d) +@@ -578,7 +589,7 @@ static int byt_gpio_probe(struct platform_device *pdev) + if (IS_ERR(vg->reg_base)) + return PTR_ERR(vg->reg_base); + +- spin_lock_init(&vg->lock); ++ raw_spin_lock_init(&vg->lock); + + gc = &vg->chip; + gc->label = dev_name(&pdev->dev); +diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c +index 454536c49315..9c780740fb82 100644 +--- a/drivers/scsi/mvsas/mv_sas.c ++++ b/drivers/scsi/mvsas/mv_sas.c +@@ -887,6 +887,8 @@ static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc) + static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task, + struct mvs_slot_info *slot, u32 slot_idx) + { ++ if (!slot) ++ return; + if (!slot->task) + return; + if (!sas_protocol_ata(task->task_proto)) +diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c +index 23685e74917e..bd2c69f85949 100644 +--- a/drivers/staging/iio/accel/sca3000_ring.c ++++ b/drivers/staging/iio/accel/sca3000_ring.c +@@ -116,7 +116,7 @@ static int sca3000_read_first_n_hw_rb(struct iio_buffer *r, + if (ret) + goto error_ret; + +- for (i = 0; i < num_read; i++) ++ for (i = 0; i < num_read / sizeof(u16); i++) + *(((u16 *)rx) + i) = be16_to_cpup((__be16 *)rx + i); + + if (copy_to_user(buf, rx, num_read)) +diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c +index d7c5223f1c3e..2931ea9b75d1 100644 +--- a/drivers/staging/iio/adc/mxs-lradc.c ++++ b/drivers/staging/iio/adc/mxs-lradc.c +@@ -919,11 +919,12 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev, + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + /* The calculated value from the ADC is in Kelvin, we +- * want Celsius for hwmon so the offset is +- * -272.15 * scale ++ * want Celsius for hwmon so the offset is -273.15 ++ * The offset is applied before scaling so it is ++ * actually -213.15 * 4 / 1.012 = -1079.644268 + */ +- *val = -1075; +- *val2 = 691699; ++ *val = -1079; ++ *val2 = 644268; + + return IIO_VAL_INT_PLUS_MICRO; + } +diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c +index c96ff10b869e..af68d06fd193 100644 +--- a/drivers/thermal/samsung/exynos_tmu.c ++++ b/drivers/thermal/samsung/exynos_tmu.c +@@ -933,7 +933,7 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, + + if (data->soc == SOC_ARCH_EXYNOS5260) + emul_con = EXYNOS5260_EMUL_CON; +- if (data->soc == SOC_ARCH_EXYNOS5433) ++ else if (data->soc == SOC_ARCH_EXYNOS5433) + emul_con = EXYNOS5433_TMU_EMUL_CON; + else if (data->soc == SOC_ARCH_EXYNOS7) + emul_con = EXYNOS7_TMU_REG_EMUL_CON; +diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c +index 21d01a491405..e508939daea3 100644 +--- a/drivers/tty/serial/8250/8250_dma.c ++++ b/drivers/tty/serial/8250/8250_dma.c +@@ -80,10 +80,6 @@ int serial8250_tx_dma(struct uart_8250_port *p) + return 0; + + dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); +- if (dma->tx_size < p->port.fifosize) { +- ret = -EINVAL; +- goto err; +- } + + desc = dmaengine_prep_slave_single(dma->txchan, + dma->tx_addr + xmit->tail, +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +index c79d33676672..c47d3e480586 100644 +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -147,6 +147,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) { + xhci->quirks |= XHCI_SPURIOUS_REBOOT; ++ xhci->quirks |= XHCI_SPURIOUS_WAKEUP; + } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + (pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI || +diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c +index 8aadf3def901..63041c1e5a9f 100644 +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -2239,6 +2239,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, + u32 trb_comp_code; + int ret = 0; + int td_num = 0; ++ bool handling_skipped_tds = false; + + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); + xdev = xhci->devs[slot_id]; +@@ -2372,6 +2373,10 @@ static int handle_tx_event(struct xhci_hcd *xhci, + ep->skip = true; + xhci_dbg(xhci, "Miss service interval error, set skip flag\n"); + goto cleanup; ++ case COMP_PING_ERR: ++ ep->skip = true; ++ xhci_dbg(xhci, "No Ping response error, Skip one Isoc TD\n"); ++ goto cleanup; + default: + if (xhci_is_vendor_info_code(xhci, trb_comp_code)) { + status = 0; +@@ -2508,13 +2513,18 @@ static int handle_tx_event(struct xhci_hcd *xhci, + ep, &status); + + cleanup: ++ ++ ++ handling_skipped_tds = ep->skip && ++ trb_comp_code != COMP_MISSED_INT && ++ trb_comp_code != COMP_PING_ERR; ++ + /* +- * Do not update event ring dequeue pointer if ep->skip is set. +- * Will roll back to continue process missed tds. ++ * Do not update event ring dequeue pointer if we're in a loop ++ * processing missed tds. + */ +- if (trb_comp_code == COMP_MISSED_INT || !ep->skip) { ++ if (!handling_skipped_tds) + inc_deq(xhci, xhci->event_ring); +- } + + if (ret) { + urb = td->urb; +@@ -2549,7 +2559,7 @@ cleanup: + * Process them as short transfer until reach the td pointed by + * the event. + */ +- } while (ep->skip && trb_comp_code != COMP_MISSED_INT); ++ } while (handling_skipped_tds); + + return 0; + } +diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c +index ebcec8cda858..f49d262e926b 100644 +--- a/drivers/usb/serial/qcserial.c ++++ b/drivers/usb/serial/qcserial.c +@@ -153,6 +153,8 @@ static const struct usb_device_id id_table[] = { + {DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */ + {DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */ + {DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */ ++ {DEVICE_SWI(0x1199, 0x9070)}, /* Sierra Wireless MC74xx/EM74xx */ ++ {DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx/EM74xx */ + {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ + {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ + {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ +diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c +index 1aaf89300621..92f394927f24 100644 +--- a/drivers/video/console/fbcon.c ++++ b/drivers/video/console/fbcon.c +@@ -1093,6 +1093,7 @@ static void fbcon_init(struct vc_data *vc, int init) + con_copy_unimap(vc, svc); + + ops = info->fbcon_par; ++ ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); + p->con_rotate = initial_rotation; + set_blitting_type(vc, info); + +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index f490b6155091..641d3dc4f31e 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -4649,7 +4649,7 @@ locked: + + if (bctl->flags & ~(BTRFS_BALANCE_ARGS_MASK | BTRFS_BALANCE_TYPE_MASK)) { + ret = -EINVAL; +- goto out_bargs; ++ goto out_bctl; + } + + do_balance: +@@ -4663,12 +4663,15 @@ do_balance: + need_unlock = false; + + ret = btrfs_balance(bctl, bargs); ++ bctl = NULL; + + if (arg) { + if (copy_to_user(arg, bargs, sizeof(*bargs))) + ret = -EFAULT; + } + ++out_bctl: ++ kfree(bctl); + out_bargs: + kfree(bargs); + out_unlock: +diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c +index 84d693d37428..871fcb67be97 100644 +--- a/fs/overlayfs/copy_up.c ++++ b/fs/overlayfs/copy_up.c +@@ -81,11 +81,11 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) + if (len == 0) + return 0; + +- old_file = ovl_path_open(old, O_RDONLY); ++ old_file = ovl_path_open(old, O_LARGEFILE | O_RDONLY); + if (IS_ERR(old_file)) + return PTR_ERR(old_file); + +- new_file = ovl_path_open(new, O_WRONLY); ++ new_file = ovl_path_open(new, O_LARGEFILE | O_WRONLY); + if (IS_ERR(new_file)) { + error = PTR_ERR(new_file); + goto out_fput; +@@ -267,7 +267,7 @@ out: + + out_cleanup: + ovl_cleanup(wdir, newdentry); +- goto out; ++ goto out2; + } + + /* +diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c +index d9da5a4e9382..ec0c2a050043 100644 +--- a/fs/overlayfs/inode.c ++++ b/fs/overlayfs/inode.c +@@ -363,6 +363,9 @@ struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) + ovl_path_upper(dentry, &realpath); + } + ++ if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE) ++ return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags); ++ + return d_backing_inode(realpath.dentry); + } + +diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c +index 79073d68b475..e38ee0fed24a 100644 +--- a/fs/overlayfs/super.c ++++ b/fs/overlayfs/super.c +@@ -544,6 +544,7 @@ static void ovl_put_super(struct super_block *sb) + mntput(ufs->upper_mnt); + for (i = 0; i < ufs->numlower; i++) + mntput(ufs->lower_mnt[i]); ++ kfree(ufs->lower_mnt); + + kfree(ufs->config.lowerdir); + kfree(ufs->config.upperdir); +@@ -1048,6 +1049,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) + oe->lowerstack[i].dentry = stack[i].dentry; + oe->lowerstack[i].mnt = ufs->lower_mnt[i]; + } ++ kfree(stack); + + root_dentry->d_fsdata = oe; + +diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h +index 0fe9df983ab7..fe0ab983859b 100644 +--- a/include/linux/backing-dev.h ++++ b/include/linux/backing-dev.h +@@ -18,13 +18,17 @@ + #include + + int __must_check bdi_init(struct backing_dev_info *bdi); +-void bdi_destroy(struct backing_dev_info *bdi); ++void bdi_exit(struct backing_dev_info *bdi); + + __printf(3, 4) + int bdi_register(struct backing_dev_info *bdi, struct device *parent, + const char *fmt, ...); + int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); ++void bdi_unregister(struct backing_dev_info *bdi); ++ + int __must_check bdi_setup_and_register(struct backing_dev_info *, char *); ++void bdi_destroy(struct backing_dev_info *bdi); ++ + void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, + bool range_cyclic, enum wb_reason reason); + void wb_start_background_writeback(struct bdi_writeback *wb); +diff --git a/include/linux/omap-dma.h b/include/linux/omap-dma.h +index e5a70132a240..88fa8af2b937 100644 +--- a/include/linux/omap-dma.h ++++ b/include/linux/omap-dma.h +@@ -17,7 +17,7 @@ + + #include + +-#define INT_DMA_LCD 25 ++#define INT_DMA_LCD (NR_IRQS_LEGACY + 25) + + #define OMAP1_DMA_TOUT_IRQ (1 << 0) + #define OMAP_DMA_DROP_IRQ (1 << 1) +diff --git a/include/sound/soc.h b/include/sound/soc.h +index 93df8bf9d54a..334d0d292020 100644 +--- a/include/sound/soc.h ++++ b/include/sound/soc.h +@@ -86,7 +86,7 @@ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array),\ +- .info = snd_soc_info_volsw, \ ++ .info = snd_soc_info_volsw_sx, \ + .get = snd_soc_get_volsw_sx,\ + .put = snd_soc_put_volsw_sx, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ +@@ -156,7 +156,7 @@ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ +- .info = snd_soc_info_volsw, \ ++ .info = snd_soc_info_volsw_sx, \ + .get = snd_soc_get_volsw_sx, \ + .put = snd_soc_put_volsw_sx, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ +@@ -573,6 +573,8 @@ int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); ++int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo); + #define snd_soc_info_bool_ext snd_ctl_boolean_mono_info + int snd_soc_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h +index 898be3a8db9a..6d8f8fba3341 100644 +--- a/include/sound/wm8904.h ++++ b/include/sound/wm8904.h +@@ -119,7 +119,7 @@ + #define WM8904_MIC_REGS 2 + #define WM8904_GPIO_REGS 4 + #define WM8904_DRC_REGS 4 +-#define WM8904_EQ_REGS 25 ++#define WM8904_EQ_REGS 24 + + /** + * DRC configurations are specified with a label and a set of register +diff --git a/kernel/module.c b/kernel/module.c +index b86b7bf1be38..8f051a106676 100644 +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -1063,11 +1063,15 @@ void symbol_put_addr(void *addr) + if (core_kernel_text(a)) + return; + +- /* module_text_address is safe here: we're supposed to have reference +- * to module from symbol_get, so it can't go away. */ ++ /* ++ * Even though we hold a reference on the module; we still need to ++ * disable preemption in order to safely traverse the data structure. ++ */ ++ preempt_disable(); + modaddr = __module_text_address(a); + BUG_ON(!modaddr); + module_put(modaddr); ++ preempt_enable(); + } + EXPORT_SYMBOL_GPL(symbol_put_addr); + +diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c +index 0a17af35670a..da7f8266913b 100644 +--- a/kernel/sched/deadline.c ++++ b/kernel/sched/deadline.c +@@ -1066,8 +1066,9 @@ select_task_rq_dl(struct task_struct *p, int cpu, int sd_flag, int flags) + int target = find_later_rq(p); + + if (target != -1 && +- dl_time_before(p->dl.deadline, +- cpu_rq(target)->dl.earliest_dl.curr)) ++ (dl_time_before(p->dl.deadline, ++ cpu_rq(target)->dl.earliest_dl.curr) || ++ (cpu_rq(target)->dl.dl_nr_running == 0))) + cpu = target; + } + rcu_read_unlock(); +@@ -1417,7 +1418,8 @@ static struct rq *find_lock_later_rq(struct task_struct *task, struct rq *rq) + + later_rq = cpu_rq(cpu); + +- if (!dl_time_before(task->dl.deadline, ++ if (later_rq->dl.dl_nr_running && ++ !dl_time_before(task->dl.deadline, + later_rq->dl.earliest_dl.curr)) { + /* + * Target rq has tasks of equal or earlier deadline, +diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c +index 3f34496244e9..96969012f242 100644 +--- a/kernel/trace/trace_stack.c ++++ b/kernel/trace/trace_stack.c +@@ -94,6 +94,12 @@ check_stack(unsigned long ip, unsigned long *stack) + local_irq_save(flags); + arch_spin_lock(&max_stack_lock); + ++ /* ++ * RCU may not be watching, make it see us. ++ * The stack trace code uses rcu_sched. ++ */ ++ rcu_irq_enter(); ++ + /* In case another CPU set the tracer_frame on us */ + if (unlikely(!frame_size)) + this_size -= tracer_frame; +@@ -174,6 +180,7 @@ check_stack(unsigned long ip, unsigned long *stack) + } + + out: ++ rcu_irq_exit(); + arch_spin_unlock(&max_stack_lock); + local_irq_restore(flags); + } +diff --git a/lib/fault-inject.c b/lib/fault-inject.c +index f1cdeb024d17..6a823a53e357 100644 +--- a/lib/fault-inject.c ++++ b/lib/fault-inject.c +@@ -44,7 +44,7 @@ static void fail_dump(struct fault_attr *attr) + printk(KERN_NOTICE "FAULT_INJECTION: forcing a failure.\n" + "name %pd, interval %lu, probability %lu, " + "space %d, times %d\n", attr->dname, +- attr->probability, attr->interval, ++ attr->interval, attr->probability, + atomic_read(&attr->space), + atomic_read(&attr->times)); + if (attr->verbose > 1) +diff --git a/mm/backing-dev.c b/mm/backing-dev.c +index dac5bf59309d..dc07d8866d9a 100644 +--- a/mm/backing-dev.c ++++ b/mm/backing-dev.c +@@ -823,7 +823,7 @@ static void bdi_remove_from_list(struct backing_dev_info *bdi) + synchronize_rcu_expedited(); + } + +-void bdi_destroy(struct backing_dev_info *bdi) ++void bdi_unregister(struct backing_dev_info *bdi) + { + /* make sure nobody finds us on the bdi_list anymore */ + bdi_remove_from_list(bdi); +@@ -835,9 +835,19 @@ void bdi_destroy(struct backing_dev_info *bdi) + device_unregister(bdi->dev); + bdi->dev = NULL; + } ++} + ++void bdi_exit(struct backing_dev_info *bdi) ++{ ++ WARN_ON_ONCE(bdi->dev); + wb_exit(&bdi->wb); + } ++ ++void bdi_destroy(struct backing_dev_info *bdi) ++{ ++ bdi_unregister(bdi); ++ bdi_exit(bdi); ++} + EXPORT_SYMBOL(bdi_destroy); + + /* +diff --git a/mm/filemap.c b/mm/filemap.c +index 1283fc825458..3fd68ee183c6 100644 +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -2488,6 +2488,11 @@ again: + break; + } + ++ if (fatal_signal_pending(current)) { ++ status = -EINTR; ++ break; ++ } ++ + status = a_ops->write_begin(file, mapping, pos, bytes, flags, + &page, &fsdata); + if (unlikely(status < 0)) +@@ -2525,10 +2530,6 @@ again: + written += copied; + + balance_dirty_pages_ratelimited(mapping); +- if (fatal_signal_pending(current)) { +- status = -EINTR; +- break; +- } + } while (iov_iter_count(i)); + + return written ? written : status; +diff --git a/mm/huge_memory.c b/mm/huge_memory.c +index 097c7a4bfbd9..da0ac6a0445f 100644 +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -2132,7 +2132,8 @@ static int __collapse_huge_page_isolate(struct vm_area_struct *vma, + for (_pte = pte; _pte < pte+HPAGE_PMD_NR; + _pte++, address += PAGE_SIZE) { + pte_t pteval = *_pte; +- if (pte_none(pteval) || is_zero_pfn(pte_pfn(pteval))) { ++ if (pte_none(pteval) || (pte_present(pteval) && ++ is_zero_pfn(pte_pfn(pteval)))) { + if (++none_or_zero <= khugepaged_max_ptes_none) + continue; + else +diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c +index 3ea8b7de9633..58d9a8167dd2 100644 +--- a/net/mac80211/debugfs.c ++++ b/net/mac80211/debugfs.c +@@ -148,7 +148,7 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, + + for (i = 0; i < NUM_IEEE80211_HW_FLAGS; i++) { + if (test_bit(i, local->hw.flags)) +- pos += scnprintf(pos, end - pos, "%s", ++ pos += scnprintf(pos, end - pos, "%s\n", + hw_flag_names[i]); + } + +diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c +index a1fe5377a2b3..5a30ce6e8c90 100644 +--- a/net/netfilter/ipset/ip_set_list_set.c ++++ b/net/netfilter/ipset/ip_set_list_set.c +@@ -297,7 +297,7 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, + ip_set_timeout_expired(ext_timeout(n, set)))) + n = NULL; + +- e = kzalloc(set->dsize, GFP_KERNEL); ++ e = kzalloc(set->dsize, GFP_ATOMIC); + if (!e) + return -ENOMEM; + e->id = d->id; +diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c +index 0aa5d9eb6c3f..d85aa1a75188 100644 +--- a/sound/hda/ext/hdac_ext_bus.c ++++ b/sound/hda/ext/hdac_ext_bus.c +@@ -19,6 +19,7 @@ + + #include + #include ++#include + #include + + MODULE_DESCRIPTION("HDA extended core"); +diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c +index d1a2cb65e27c..ca374462d7e5 100644 +--- a/sound/pci/hda/hda_codec.c ++++ b/sound/pci/hda/hda_codec.c +@@ -3438,10 +3438,8 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec) + int dev, err; + + err = snd_hda_codec_parse_pcms(codec); +- if (err < 0) { +- snd_hda_codec_reset(codec); ++ if (err < 0) + return err; +- } + + /* attach a new PCM streams */ + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { +diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c +index ca03c40609fc..2f0ec7c45fc7 100644 +--- a/sound/pci/hda/patch_conexant.c ++++ b/sound/pci/hda/patch_conexant.c +@@ -819,6 +819,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { + SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), + SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), ++ SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), + SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC), +diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c +index 100d92b5b77e..05977ae1ff2a 100644 +--- a/sound/soc/soc-ops.c ++++ b/sound/soc/soc-ops.c +@@ -207,6 +207,34 @@ int snd_soc_info_volsw(struct snd_kcontrol *kcontrol, + EXPORT_SYMBOL_GPL(snd_soc_info_volsw); + + /** ++ * snd_soc_info_volsw_sx - Mixer info callback for SX TLV controls ++ * @kcontrol: mixer control ++ * @uinfo: control element information ++ * ++ * Callback to provide information about a single mixer control, or a double ++ * mixer control that spans 2 registers of the SX TLV type. SX TLV controls ++ * have a range that represents both positive and negative values either side ++ * of zero but without a sign bit. ++ * ++ * Returns 0 for success. ++ */ ++int snd_soc_info_volsw_sx(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ struct soc_mixer_control *mc = ++ (struct soc_mixer_control *)kcontrol->private_value; ++ ++ snd_soc_info_volsw(kcontrol, uinfo); ++ /* Max represents the number of levels in an SX control not the ++ * maximum value, so add the minimum value back on ++ */ ++ uinfo->value.integer.max += mc->min; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(snd_soc_info_volsw_sx); ++ ++/** + * snd_soc_get_volsw - single mixer get callback + * @kcontrol: mixer control + * @ucontrol: control element information +diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c +index 21c14244f4c4..d7ea8e20dae4 100644 +--- a/virt/kvm/irqchip.c ++++ b/virt/kvm/irqchip.c +@@ -213,11 +213,15 @@ int kvm_set_irq_routing(struct kvm *kvm, + goto out; + + r = -EINVAL; +- if (ue->flags) ++ if (ue->flags) { ++ kfree(e); + goto out; ++ } + r = setup_routing_entry(new, e, ue); +- if (r) ++ if (r) { ++ kfree(e); + goto out; ++ } + ++ue; + } + diff --git a/patch/kernel/s500-default/fbtft_drivers.patch b/patch/kernel/s500-default/fbtft_drivers.patch new file mode 100644 index 000000000..8ecb55230 --- /dev/null +++ b/patch/kernel/s500-default/fbtft_drivers.patch @@ -0,0 +1,12012 @@ +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 03bed11..48f1f71 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/host1x/Kconfig" +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 07905d0..14810e4 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-$(CONFIG_HDMI) += hdmi.o + obj-y += fb_notify.o +diff --git a/drivers/video/fbtft/Kconfig b/drivers/video/fbtft/Kconfig +new file mode 100644 +index 0000000..995a910 +--- /dev/null ++++ b/drivers/video/fbtft/Kconfig +@@ -0,0 +1,169 @@ ++menuconfig FB_TFT ++ tristate "Support for small TFT LCD display modules" ++ depends on FB && SPI && GPIOLIB ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ select FB_SYS_FOPS ++ select FB_DEFERRED_IO ++ select FB_BACKLIGHT ++ ++config FB_TFT_AGM1264K_FL ++ tristate "FB driver for the AGM1264K-FL LCD display" ++ depends on FB_TFT ++ help ++ Framebuffer support for the AGM1264K-FL LCD display (two Samsung KS0108 compatable chips) ++ ++config FB_TFT_BD663474 ++ tristate "FB driver for the BD663474 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for BD663474 ++ ++config FB_TFT_HX8340BN ++ tristate "FB driver for the HX8340BN LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8340BN ++ ++config FB_TFT_HX8347D ++ tristate "FB driver for the HX8347D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8347D ++ ++config FB_TFT_HX8353D ++ tristate "FB driver for the HX8353D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8353D ++ ++config FB_TFT_ILI9320 ++ tristate "FB driver for the ILI9320 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9320 ++ ++config FB_TFT_ILI9325 ++ tristate "FB driver for the ILI9325 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9325 ++ ++config FB_TFT_ILI9340 ++ tristate "FB driver for the ILI9340 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9340 ++ ++config FB_TFT_ILI9341 ++ tristate "FB driver for the ILI9341 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9341 ++ ++config FB_TFT_ILI9481 ++ tristate "FB driver for the ILI9481 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9481 ++ ++config FB_TFT_ILI9486 ++ tristate "FB driver for the ILI9486 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9486 ++ ++config FB_TFT_PCD8544 ++ tristate "FB driver for the PCD8544 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for PCD8544 ++ ++config FB_TFT_RA8875 ++ tristate "FB driver for the RA8875 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for RA8875 ++ ++config FB_TFT_S6D02A1 ++ tristate "FB driver for the S6D02A1 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D02A1 ++ ++config FB_TFT_S6D1121 ++ tristate "FB driver for the S6D1211 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D1121 ++ ++config FB_TFT_SSD1289 ++ tristate "FB driver for the SSD1289 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1289 ++ ++config FB_TFT_SSD1306 ++ tristate "FB driver for the SSD1306 OLED Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1306 ++ ++config FB_TFT_SSD1331 ++ tristate "FB driver for the SSD1331 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1331 ++ ++config FB_TFT_SSD1351 ++ tristate "FB driver for the SSD1351 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1351 ++ ++config FB_TFT_ST7735R ++ tristate "FB driver for the ST7735R LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ST7735R ++ ++config FB_TFT_TINYLCD ++ tristate "FB driver for tinylcd.com display" ++ depends on FB_TFT ++ help ++ Custom Framebuffer support for tinylcd.com display ++ ++config FB_TFT_TLS8204 ++ tristate "FB driver for the TLS8204 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TLS8204 ++ ++config FB_TFT_UC1701 ++ tristate "FB driver for the UC1701 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for UC1701 ++ ++config FB_TFT_UPD161704 ++ tristate "FB driver for the uPD161704 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for uPD161704 ++ ++config FB_TFT_WATTEROTT ++ tristate "FB driver for the WATTEROTT LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for WATTEROTT ++ ++config FB_FLEX ++ tristate "Generic FB driver for TFT LCD displays" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TFT LCD displays. ++ ++config FB_TFT_FBTFT_DEVICE ++ tristate "Module to for adding FBTFT devices" ++ depends on FB_TFT +diff --git a/drivers/video/fbtft/Makefile b/drivers/video/fbtft/Makefile +new file mode 100644 +index 0000000..71c755d +--- /dev/null ++++ b/drivers/video/fbtft/Makefile +@@ -0,0 +1,60 @@ ++ifneq ($(KERNELRELEASE),) ++# kbuild part of makefile ++ ++# Optionally, include config file to allow out of tree kernel modules build ++-include $(src)/.config ++ ++# Core module ++obj-$(CONFIG_FB_TFT) += fbtft.o ++fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o ++ ++# drivers ++obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o ++obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o ++obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o ++obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o ++obj-$(CONFIG_FB_TFT_HX8353D) += fb_hx8353d.o ++obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o ++obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o ++obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o ++obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o ++obj-$(CONFIG_FB_TFT_ILI9481) += fb_ili9481.o ++obj-$(CONFIG_FB_TFT_ILI9486) += fb_ili9486.o ++obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o ++obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o ++obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o ++obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o ++obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o ++obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o ++obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o ++obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o ++obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o ++obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o ++obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o ++obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o ++obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o ++obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o ++obj-$(CONFIG_FB_FLEX) += flexfb.o ++ ++# Device modules ++obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o ++ ++else ++# normal makefile ++KDIR ?= /lib/modules/`uname -r`/build ++ ++default: .config ++ $(MAKE) -C $(KDIR) M=$$PWD modules ++ ++.config: ++ grep config Kconfig | cut -d' ' -f2 | sed 's@^@CONFIG_@; s@$$@=m@' > .config ++ ++install: ++ $(MAKE) -C $(KDIR) M=$$PWD modules_install ++ ++ ++clean: ++ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions \ ++ modules.order Module.symvers ++ ++endif +diff --git a/drivers/video/fbtft/README b/drivers/video/fbtft/README +new file mode 100644 +index 0000000..9bebc98 +--- /dev/null ++++ b/drivers/video/fbtft/README +@@ -0,0 +1,37 @@ ++ FBTFT ++========= ++ ++2015-01-19 ++The FBTFT drivers are now in the Linux kernel staging tree: https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing ++Development in this github repo has ceased. ++ ++ ++Linux Framebuffer drivers for small TFT LCD display modules. ++The module 'fbtft' makes writing drivers for some of these displays very easy. ++ ++Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. ++ ++INSTALLATION ++ Download kernel sources ++ ++ From Linux 3.15 ++ cd drivers/video/fbdev ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" ++ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ ++ ++ Before Linux 3.15 ++ cd drivers/video ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" ++ Add to drivers/video/Makefile: obj-y += fbtft/ ++ ++ Enable driver(s) in menuconfig and build the kernel ++ ++ ++See wiki for more information: https://github.com/notro/fbtft/wiki ++ ++ ++Source: https://github.com/notro/fbtft/ +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +new file mode 100644 +index 0000000..621497f +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +@@ -0,0 +1,87 @@ ++/* ++ * Device Tree overlay for HY28A display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28a_pins: hy28a_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28a: hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28a_pins>; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts: hy28a-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28a>,"spi-max-frequency:0"; ++ rotate = <&hy28a>,"rotate:0"; ++ fps = <&hy28a>,"fps:0"; ++ debug = <&hy28a>,"debug:0"; ++ xohms = <&hy28a_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28a>,"reset-gpios:4", ++ <&hy28a_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28a>,"led-gpios:4", ++ <&hy28a_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +new file mode 100644 +index 0000000..f774c4a +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +@@ -0,0 +1,142 @@ ++/* ++ * Device Tree overlay for HY28b display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28b_pins: hy28b_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28b: hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28b_pins>; ++ ++ spi-max-frequency = <48000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts: hy28b-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28b>,"spi-max-frequency:0"; ++ rotate = <&hy28b>,"rotate:0"; ++ fps = <&hy28b>,"fps:0"; ++ debug = <&hy28b>,"debug:0"; ++ xohms = <&hy28b_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28b>,"reset-gpios:4", ++ <&hy28b_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28b>,"led-gpios:4", ++ <&hy28b_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +new file mode 100644 +index 0000000..c06fe12 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +@@ -0,0 +1,109 @@ ++/* ++ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mz61581_pins: mz61581_pins { ++ brcm,pins = <4 15 18 25>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ mz61581: mz61581@0{ ++ compatible = "samsung,s6d02a1"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mz61581_pins>; ++ ++ spi-max-frequency = <128000000>; ++ spi-cpol; ++ spi-cpha; ++ ++ width = <320>; ++ height = <480>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ ++ reset-gpios = <&gpio 15 0>; ++ dc-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 0>; ++ ++ init = <0x10000b0 00 ++ 0x1000011 ++ 0x20000ff ++ 0x10000b3 0x02 0x00 0x00 0x00 ++ 0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43 ++ 0x10000c1 0x08 0x16 0x08 0x08 ++ 0x10000c4 0x11 0x07 0x03 0x03 ++ 0x10000c6 0x00 ++ 0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00 ++ 0x1000035 0x00 ++ 0x1000036 0xa0 ++ 0x100003a 0x55 ++ 0x1000044 0x00 0x01 ++ 0x10000d0 0x07 0x07 0x1d 0x03 ++ 0x10000d1 0x03 0x30 0x10 ++ 0x10000d2 0x03 0x14 0x04 ++ 0x1000029 ++ 0x100002c>; ++ ++ /* This is a workaround to make sure the init sequence slows down and doesn't fail */ ++ debug = <3>; ++ }; ++ ++ mz61581_ts: mz61581_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <4 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 4 0>; ++ ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&mz61581>, "spi-max-frequency:0"; ++ rotate = <&mz61581>, "rotate:0"; ++ fps = <&mz61581>, "fps:0"; ++ debug = <&mz61581>, "debug:0"; ++ xohms = <&mz61581_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +new file mode 100644 +index 0000000..8cd6a95 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +@@ -0,0 +1,94 @@ ++/* ++ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ piscreen_pins: piscreen_pins { ++ brcm,pins = <17 25 24 22>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ piscreen: piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&piscreen_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ regwidth = <16>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ debug = <0>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000ff ++ 0x100003a 0x55 ++ 0x1000036 0x28 ++ 0x10000c2 0x44 ++ 0x10000c5 0x00 0x00 0x00 0x00 ++ 0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00 ++ 0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0x1000029>; ++ }; ++ ++ piscreen-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&piscreen>,"spi-max-frequency:0"; ++ rotate = <&piscreen>,"rotate:0"; ++ fps = <&piscreen>,"fps:0"; ++ debug = <&piscreen>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +new file mode 100644 +index 0000000..e8a9365 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +@@ -0,0 +1,115 @@ ++/* ++ * Device Tree overlay for pitft resistive by Adafruit ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pitft_pins: pitft_pins { ++ brcm,pins = <24 25>; ++ brcm,function = <0 1>; /* in out */ ++ brcm,pull = <2 0>; /* pullup none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pitft: pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pitft_pins>; ++ ++ spi-max-frequency = <16000000>; ++ rotate = <90>; ++ fps = <25>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ debug = <0>; ++ }; ++ ++ pitft_ts@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "st,stmpe610"; ++ reg = <1>; ++ ++ spi-max-frequency = <500000>; ++ irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */ ++ interrupts = <24 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ ++ stmpe_gpio: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ /* ++ * only GPIO2 is wired/available ++ * and it is wired to the backlight ++ */ ++ st,norequest-mask = <0x7b>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/soc"; ++ __overlay__ { ++ backlight { ++ compatible = "gpio-backlight"; ++ gpios = <&stmpe_gpio 2 0>; ++ default-on; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&pitft>,"spi-max-frequency:0"; ++ rotate = <&pitft>,"rotate:0"; ++ fps = <&pitft>,"fps:0"; ++ debug = <&pitft>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +new file mode 100644 +index 0000000..0578810 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +@@ -0,0 +1,81 @@ ++/* ++ * Device Tree overlay for rpi-display by Watterott ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_display_pins: rpi_display_pins { ++ brcm,pins = <18 23 24 25>; ++ brcm,function = <1 1 1 0>; /* out out out in */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rpidisplay: rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_display_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpidisplay_ts: rpi-display-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <25 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&rpidisplay>,"spi-max-frequency:0"; ++ rotate = <&rpidisplay>,"rotate:0"; ++ fps = <&rpidisplay>,"fps:0"; ++ debug = <&rpidisplay>,"debug:0"; ++ xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +new file mode 100644 +index 0000000..881d2eb +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +@@ -0,0 +1,181 @@ ++/* ++ * tinylcd 3.5" display ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ tinylcd35_pins: tinylcd35_pins { ++ brcm,pins = <25 24 18>; ++ brcm,function = <1>; /* out */ ++ }; ++ tinylcd35_ts_pins: tinylcd35_ts_pins { ++ brcm,pins = <5>; ++ brcm,function = <0>; /* in */ ++ }; ++ keypad_pins: keypad_pins { ++ brcm,pins = <4 17 22 23 27>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <1>; /* down */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ tinylcd35: tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tinylcd35_pins>, ++ <&tinylcd35_ts_pins>; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ fps = <20>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ ++ init = <0x10000B0 0x80 ++ 0x10000C0 0x0A 0x0A ++ 0x10000C1 0x01 0x01 ++ 0x10000C2 0x33 ++ 0x10000C5 0x00 0x42 0x80 ++ 0x10000B1 0xD0 0x11 ++ 0x10000B4 0x02 ++ 0x10000B6 0x00 0x22 0x3B ++ 0x10000B7 0x07 ++ 0x1000036 0x58 ++ 0x10000F0 0x36 0xA5 0xD3 ++ 0x10000E5 0x80 ++ 0x10000E5 0x01 ++ 0x10000B3 0x00 ++ 0x10000E5 0x00 ++ 0x10000F0 0x36 0xA5 0x53 ++ 0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00 ++ 0x100003A 0x55 ++ 0x1000011 ++ 0x2000001 ++ 0x1000029>; ++ }; ++ ++ tinylcd35_ts: tinylcd35_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <5 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 5 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pcf8563: pcf8563@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* ++ * Values for input event code is found under the ++ * 'Keys and buttons' heading in include/uapi/linux/input.h ++ */ ++ fragment@4 { ++ target-path = "/soc"; ++ __overlay__ { ++ keypad: keypad { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&keypad_pins>; ++ status = "disabled"; ++ autorepeat; ++ ++ button@17 { ++ label = "GPIO KEY_UP"; ++ linux,code = <103>; ++ gpios = <&gpio 17 0>; ++ }; ++ button@22 { ++ label = "GPIO KEY_DOWN"; ++ linux,code = <108>; ++ gpios = <&gpio 22 0>; ++ }; ++ button@27 { ++ label = "GPIO KEY_LEFT"; ++ linux,code = <105>; ++ gpios = <&gpio 27 0>; ++ }; ++ button@23 { ++ label = "GPIO KEY_RIGHT"; ++ linux,code = <106>; ++ gpios = <&gpio 23 0>; ++ }; ++ button@4 { ++ label = "GPIO KEY_ENTER"; ++ linux,code = <28>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&tinylcd35>,"spi-max-frequency:0"; ++ rotate = <&tinylcd35>,"rotate:0"; ++ fps = <&tinylcd35>,"fps:0"; ++ debug = <&tinylcd35>,"debug:0"; ++ touch = <&tinylcd35_ts>,"status"; ++ touchgpio = <&tinylcd35_ts_pins>,"brcm,pins:0", ++ <&tinylcd35_ts>,"interrupts:0", ++ <&tinylcd35_ts>,"pendown-gpio:4"; ++ xohms = <&tinylcd35_ts>,"ti,x-plate-ohms;0"; ++ rtc = <&i2c1>,"status", ++ <&pcf8563>,"status"; ++ keypad = <&keypad>,"status"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/rpi.dts b/drivers/video/fbtft/dts/rpi.dts +new file mode 100644 +index 0000000..6c3ab6e +--- /dev/null ++++ b/drivers/video/fbtft/dts/rpi.dts +@@ -0,0 +1,414 @@ ++ ++/* ++ * FBTFT Device Tree part for the Raspberry Pi ++ * Add this file to the end of the *rpi-b.dts file ++ * ++ * Please keep it sorted alphabetically on display name ++ * ++ * Notes: ++ * - use ads7846 instead of tsc2046 to get module autoloading ++ * - use polarity 1 to drive backlight initially low (off): ++ * led-gpios = <&gpio 18 1>; ++ */ ++ ++&spi0 { ++ /* this is provided here to make it easy to enable SPI when editing this file */ ++// status = "okay"; ++}; ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28A ++ * ++ */ ++&spi0 { ++ hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28B ++ * NOT TESTED ++ */ ++&spi0 { ++ hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * ITEAD ++ * ITDB02-2.8 ++ * ++ */ ++/ { ++ itdb28 { ++ compatible = "ilitek,ili9325"; ++ status = "disabled"; ++ ++ rotate = <0>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 17 0>; ++ dc-gpios = <&gpio 3 0>; ++ cs-gpios = <&gpio 27 0>; ++ wr-gpios = <&gpio 2 0>; ++ db-gpios = <&gpio 9 0>, ++ <&gpio 11 0>, ++ <&gpio 18 0>, ++ <&gpio 23 0>, ++ <&gpio 24 0>, ++ <&gpio 25 0>, ++ <&gpio 8 0>, ++ <&gpio 7 0>; ++ /* LED pin drives backlight directly. Use transistor (50mA) */ ++ /* led-gpios = <&gpio 4 1>; */ ++ debug = <0>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Watterott ++ * RPi-Display - 2.8" Touch-Display (320x240) ++ * ++ */ ++&spi0 { ++ rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpi-display_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 25>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Ozzmaker ++ * PiScreen - 3.5" TFT touchscreen (480x320) ++ * ++ */ ++&spi0 { ++ piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <20000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000FF ++ 0x100003A 0x55 ++ 0x1000036 0x28 ++ 0x10000C2 0x44 ++ 0x10000C5 0x00 0x00 0x00 0x00 ++ 0x10000E0 0x0F 0x1F 0x1C 0x0C 0x0F 0x08 0x48 0x98 0x37 0x0A 0x13 0x04 0x11 0x0D 0x00 ++ 0x10000E1 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000E2 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0X1000029>; ++ debug = <0>; ++ }; ++ ++ piscreen_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Adafruit ++ * PiTFT Mini Kit - 320x240 2.8" TFT+Touchscreen for Raspberry Pi ++ * ++ * Couldn't get touch controller to work ++ */ ++ ++&gpio { ++ stmpets_pins: stmpets_pins { ++ brcm,pins = <24>; ++ brcm,function = <0>; /* gpio in */ ++ brcm,pull = <2>; /* pull up */ ++ }; ++}; ++ ++&spi0 { ++ pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <90>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ ++ init = <0x1000001 ++ 0x2000005 ++ 0x1000028 ++ 0x10000EF 0x03 0x80 0x02 ++ 0x10000CF 0x00 0xC1 0x30 ++ 0x10000ED 0x64 0x03 0x12 0x81 ++ 0x10000E8 0x85 0x00 0x78 ++ 0x10000CB 0x39 0x2C 0x00 0x34 0x02 ++ 0x10000F7 0x20 ++ 0x10000EA 0x00 0x00 ++ 0x10000C0 0x23 ++ 0x10000C1 0x10 ++ 0x10000C5 0x3e 0x28 ++ 0x10000C7 0x86 ++ 0x100003A 0x55 ++ 0x10000B1 0x00 0x18 ++ 0x10000B6 0x08 0x82 0x27 ++ 0x10000F2 0x00 ++ 0x1000026 0x01 ++ 0x10000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00 ++ 0x10000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F ++ 0x1000011 ++ 0x2000064 ++ 0x1000029 ++ 0x2000014>; ++ debug = <0>; ++ }; ++/* ++Touchscreen didn't work. I guess it has something to do with it being an interrupt controller. ++I have never tried this before with DT and ARCH_BCM2708. ++On ARCH_BCM2835 it should be OK, since the GPIO controller acts as an interrupt controller as well. ++(stmpe_device can't be used from 3.16, because irq_base can't be set anymore) ++ ++[ 8.889056] irq: irq_create_mapping(0xc3008800, 0xc2) ++[ 8.895698] irq: -> using domain @c3008800 ++[ 8.900796] irq: -> existing mapping on virq 194 ++[ 8.925048] stmpe-spi spi0.1: stmpe610 detected, chip id: 0x811 ++[ 8.936650] irq: Added domain (null) ++[ 8.941780] irq: irq_create_mapping(0xc1dc1a20, 0x0) ++[ 8.949047] irq: -> using domain @c1dc1a20 ++[ 8.954421] irq: -> virq allocation failed ++[ 8.959926] irq: irq_create_mapping(0xc1dc1a20, 0x1) ++[ 8.967366] irq: -> using domain @c1dc1a20 ++[ 8.972870] irq: -> virq allocation failed ++*/ ++ pitft_ts@1 { ++ compatible = "st,stmpe610"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ status = "disabled"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&stmpets_pins>; ++ ++ spi-max-frequency = <500000>; ++ interrupts = <3 24>; ++ interrupt-parent = <&intc>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * NeoSec LLC ++ * 3.5 inch TFT Display (320x480) ++ * ++ */ ++&spi0 { ++ tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ tinylcd35@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 3>; ++ pendown-gpio = <&gpio 3 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; +diff --git a/drivers/video/fbtft/fb_agm1264k-fl.c b/drivers/video/fbtft/fb_agm1264k-fl.c +new file mode 100644 +index 0000000..7fe4fa0 +--- /dev/null ++++ b/drivers/video/fbtft/fb_agm1264k-fl.c +@@ -0,0 +1,462 @@ ++/* ++ * FB driver for Two KS0108 LCD controllers in AGM1264K-FL display ++ * ++ * Copyright (C) 2014 ololoshka2871 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++/* Uncomment text line to use negative image on display */ ++/*#define NEGATIVE*/ ++ ++#define WHITE 0xff ++#define BLACK 0 ++ ++#define DRVNAME "fb_agm1264k-fl" ++#define WIDTH 64 ++#define HEIGHT 64 ++#define TOTALWIDTH (WIDTH * 2) /* because 2 x ks0108 in one display */ ++#define FPS 20 ++ ++#define EPIN gpio.wr ++#define RS gpio.dc ++#define RW gpio.aux[2] ++#define CS0 gpio.aux[0] ++#define CS1 gpio.aux[1] ++ ++ ++/* diffusing error (“Floyd-Steinberg”) */ ++#define DIFFUSING_MATRIX_WIDTH 2 ++#define DIFFUSING_MATRIX_HEIGHT 2 ++ ++static const signed char ++diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = { ++ {-1, 3}, ++ {3, 2}, ++}; ++ ++static const unsigned char gamma_correction_table[] = { ++0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, ++1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, ++6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, ++13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, ++22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, ++33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, ++46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ++62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81, ++82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102, ++103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121, ++123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143, ++145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166, ++168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192, ++194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, ++221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248, ++251, 253, 255 ++}; ++ ++static int init_display(struct fbtft_par *par) ++{ ++ u8 i; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ for (i = 0; i < 2; ++i) { ++ write_reg(par, i, 0x3f); /* display on */ ++ write_reg(par, i, 0x40); /* set x to 0 */ ++ write_reg(par, i, 0xb0); /* set page to 0 */ ++ write_reg(par, i, 0xc0); /* set start line to 0 */ ++ } ++ ++ return 0; ++} ++ ++void reset(struct fbtft_par *par) ++{ ++ if (par->gpio.reset == -1) ++ return; ++ ++ fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__); ++ ++ gpio_set_value(par->gpio.reset, 0); ++ udelay(20); ++ gpio_set_value(par->gpio.reset, 1); ++ mdelay(120); ++} ++ ++/* Check if all necessary GPIOS defined */ ++static int verify_gpios(struct fbtft_par *par) ++{ ++ int i; ++ ++ fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device, ++ "%s()\n", __func__); ++ ++ if (par->EPIN < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'wr' (aka E) gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ for (i = 0; i < 8; ++i) { ++ if (par->gpio.db[i] < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'db[%i]' gpio. Aborting.\n", ++ i); ++ return -EINVAL; ++ } ++ } ++ if (par->CS0 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs0' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->CS1 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs1' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->RW < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'rw' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static unsigned long ++request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio) ++{ ++ fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device, ++ "%s('%s')\n", __func__, gpio->name); ++ ++ if (strcasecmp(gpio->name, "wr") == 0) { ++ /* left ks0108 E pin */ ++ par->EPIN = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } else if (strcasecmp(gpio->name, "cs0") == 0) { ++ /* left ks0108 controller pin */ ++ par->CS0 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } else if (strcasecmp(gpio->name, "cs1") == 0) { ++ /* right ks0108 controller pin */ ++ par->CS1 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } ++ ++ /* if write (rw = 0) e(1->0) perform write */ ++ /* if read (rw = 1) e(0->1) set data on D0-7*/ ++ else if (strcasecmp(gpio->name, "rw") == 0) { ++ par->RW = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } ++ ++ return FBTFT_GPIO_NO_MATCH; ++} ++ ++/* This function oses to enter commands ++ * first byte - destination controller 0 or 1 ++ * folowing - commands ++ */ ++static void write_reg8_bus8(struct fbtft_par *par, int len, ...) ++{ ++ va_list args; ++ int i, ret; ++ u8 *buf = (u8 *)par->buf; ++ ++ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { ++ va_start(args, len); ++ for (i = 0; i < len; i++) ++ buf[i] = (u8)va_arg(args, unsigned int); ++ ++ va_end(args); ++ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, ++ par->info->device, u8, buf, len, "%s: ", __func__); ++ } ++ ++ va_start(args, len); ++ ++ *buf = (u8)va_arg(args, unsigned int); ++ ++ if (*buf > 1) { ++ va_end(args); ++ dev_err(par->info->device, "%s: Incorrect chip sellect request (%d)\n", ++ __func__, *buf); ++ return; ++ } ++ ++ /* select chip */ ++ if (*buf) { ++ /* cs1 */ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 0); ++ } else { ++ /* cs0 */ ++ gpio_set_value(par->CS0, 0); ++ gpio_set_value(par->CS1, 1); ++ } ++ ++ gpio_set_value(par->RS, 0); /* RS->0 (command mode) */ ++ len--; ++ ++ if (len) { ++ i = len; ++ while (i--) ++ *buf++ = (u8)va_arg(args, unsigned int); ++ ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8))); ++ if (ret < 0) { ++ va_end(args); ++ dev_err(par->info->device, "%s: write() failed and returned %d\n", ++ __func__, ret); ++ return; ++ } ++ } ++ ++ va_end(args); ++} ++ ++static struct ++{ ++ int xs, ys_page, xe, ye_page; ++} addr_win; ++ ++/* save display writing zone */ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ addr_win.xs = xs; ++ addr_win.ys_page = ys / 8; ++ addr_win.xe = xe; ++ addr_win.ye_page = ye / 8; ++ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__, ++ addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page); ++} ++ ++static void ++construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src, ++ int xs, int xe, int y) ++{ ++ int x, i; ++ ++ for (x = xs; x < xe; ++x) { ++ u8 res = 0; ++ ++ for (i = 0; i < 8; i++) ++ if (src[(y * 8 + i) * par->info->var.xres + x]) ++ res |= 1 << i; ++#ifdef NEGATIVE ++ *dest++ = res; ++#else ++ *dest++ = ~res; ++#endif ++ } ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y; ++ int ret = 0; ++ ++ /* buffer to convert RGB565 -> grayscale16 -> Ditherd image 1bpp */ ++ signed short *convert_buf = kmalloc(par->info->var.xres * ++ par->info->var.yres * sizeof(signed short), GFP_NOIO); ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ /* converting to grayscale16 */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ u16 pixel = vmem16[y * par->info->var.xres + x]; ++ u16 b = pixel & 0x1f; ++ u16 g = (pixel & (0x3f << 5)) >> 5; ++ u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6); ++ ++ pixel = (299 * r + 587 * g + 114 * b) / 200; ++ if (pixel > 255) ++ pixel = 255; ++ ++ /* gamma-correction by table */ ++ convert_buf[y * par->info->var.xres + x] = ++ (signed short)gamma_correction_table[pixel]; ++ } ++ ++ /* Image Dithering */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ signed short pixel = ++ convert_buf[y * par->info->var.xres + x]; ++ signed short error_b = pixel - BLACK; ++ signed short error_w = pixel - WHITE; ++ signed short error; ++ u16 i, j; ++ ++ /* what color close? */ ++ if (abs(error_b) >= abs(error_w)) { ++ /* white */ ++ error = error_w; ++ pixel = 0xff; ++ } else { ++ /* black */ ++ error = error_b; ++ pixel = 0; ++ } ++ ++ error /= 8; ++ ++ /* diffusion matrix row */ ++ for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i) ++ /* diffusion matrix column */ ++ for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) { ++ signed short *write_pos; ++ signed char coeff; ++ ++ /* skip pixels out of zone */ ++ if (x + i < 0 || ++ x + i >= par->info->var.xres ++ || y + j >= par->info->var.yres) ++ continue; ++ write_pos = &convert_buf[ ++ (y + j) * par->info->var.xres + ++ x + i]; ++ coeff = diffusing_matrix[i][j]; ++ if (coeff == -1) ++ /* pixel itself */ ++ *write_pos = pixel; ++ else { ++ signed short p = *write_pos + ++ error * coeff; ++ ++ if (p > WHITE) ++ p = WHITE; ++ if (p < BLACK) ++ p = BLACK; ++ *write_pos = p; ++ } ++ } ++ } ++ ++ /* 1 string = 2 pages */ ++ for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) { ++ /* left half of display */ ++ if (addr_win.xs < par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, convert_buf, ++ addr_win.xs, par->info->var.xres / 2, y); ++ ++ len = par->info->var.xres / 2 - addr_win.xs; ++ ++ /* select left side (sc0) ++ * set addr ++ */ ++ write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs); ++ write_reg(par, 0x00, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ ret = par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ /* right half of display */ ++ if (addr_win.xe >= par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, ++ convert_buf, par->info->var.xres / 2, ++ addr_win.xe + 1, y); ++ ++ len = addr_win.xe + 1 - par->info->var.xres / 2; ++ ++ /* select right side (sc1) ++ * set addr ++ */ ++ write_reg(par, 0x01, (1 << 6)); ++ write_reg(par, 0x01, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ } ++ kfree(convert_buf); ++ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 1); ++ ++ return ret; ++} ++ ++static int write(struct fbtft_par *par, void *buf, size_t len) ++{ ++ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, ++ "%s(len=%d): ", __func__, len); ++ ++ gpio_set_value(par->RW, 0); /* set write mode */ ++ ++ ++ while (len--) { ++ u8 i, data; ++ ++ data = *(u8 *) buf++; ++ ++ /* set data bus */ ++ for (i = 0; i < 8; ++i) ++ gpio_set_value(par->gpio.db[i], data & (1 << i)); ++ /* set E */ ++ gpio_set_value(par->EPIN, 1); ++ udelay(5); ++ /* unset E - write */ ++ gpio_set_value(par->EPIN, 0); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = TOTALWIDTH, ++ .height = HEIGHT, ++ .fps = FPS, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .verify_gpios = verify_gpios, ++ .request_gpios_match = request_gpios_match, ++ .reset = reset, ++ .write = write, ++ .write_register = write_reg8_bus8, ++ .write_vmem = write_vmem, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display); ++ ++MODULE_ALIAS("platform:" DRVNAME); ++ ++MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display"); ++MODULE_AUTHOR("ololoshka2871"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_bd663474.c b/drivers/video/fbtft/fb_bd663474.c +new file mode 100644 +index 0000000..7e00c60 +--- /dev/null ++++ b/drivers/video/fbtft/fb_bd663474.c +@@ -0,0 +1,193 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "hitachi,bd663474", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:bd663474"); ++MODULE_ALIAS("platform:bd663474"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8340bn.c b/drivers/video/fbtft/fb_hx8340bn.c +new file mode 100644 +index 0000000..3939502 +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8340bn.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the HX8340BN LCD Controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8340bn", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8340bn"); ++MODULE_ALIAS("platform:hx8340bn"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8347d.c b/drivers/video/fbtft/fb_hx8347d.c +new file mode 100644 +index 0000000..8139a8f +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8347d.c +@@ -0,0 +1,181 @@ ++/* ++ * FB driver for the HX8347D LCD Controller ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * ++ * Based on driver code found here: https://github.com/watterott/r61505u-Adapter ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8347d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8347d"); ++MODULE_ALIAS("platform:hx8347d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8353d.c b/drivers/video/fbtft/fb_hx8353d.c +new file mode 100644 +index 0000000..c9512dc +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8353d.c +@@ -0,0 +1,166 @@ ++/* ++ * FB driver for the HX8353D LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8353d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8353d"); ++MODULE_ALIAS("platform:hx8353d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9320.c b/drivers/video/fbtft/fb_ili9320.c +new file mode 100644 +index 0000000..b26d893 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9320.c +@@ -0,0 +1,234 @@ ++/* ++ * FB driver for the ILI9320 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9320", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9320"); ++MODULE_ALIAS("platform:ili9320"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9325.c b/drivers/video/fbtft/fb_ili9325.c +new file mode 100644 +index 0000000..5f88145 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9325.c +@@ -0,0 +1,291 @@ ++/* ++ * FB driver for the ILI9325 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Based on ili9325.c by Jeroen Domburg ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9325", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9325"); ++MODULE_ALIAS("platform:ili9325"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9340.c b/drivers/video/fbtft/fb_ili9340.c +new file mode 100644 +index 0000000..985687d +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9340.c +@@ -0,0 +1,163 @@ ++/* ++ * FB driver for the ILI9340 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9340", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9340"); ++MODULE_ALIAS("platform:ili9340"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9341.c b/drivers/video/fbtft/fb_ili9341.c +new file mode 100644 +index 0000000..225b2d8 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9341.c +@@ -0,0 +1,179 @@ ++/* ++ * FB driver for the ILI9341 LCD display controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * Based on adafruit22fb.c by Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9341", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9341"); ++MODULE_ALIAS("platform:ili9341"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9481.c b/drivers/video/fbtft/fb_ili9481.c +new file mode 100644 +index 0000000..725157a +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9481.c +@@ -0,0 +1,117 @@ ++/* ++ * FB driver for the ILI9481 LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_ili9481" ++#define WIDTH 320 ++#define HEIGHT 480 ++ ++static int default_init_sequence[] = { ++ ++ /* SLP_OUT - Sleep out */ ++ -1, 0x11, ++ -2, 50, ++ /* Power setting */ ++ -1, 0xD0, 0x07, 0x42, 0x18, ++ /* VCOM */ ++ -1, 0xD1, 0x00, 0x07, 0x10, ++ /* Power setting for norm. mode */ ++ -1, 0xD2, 0x01, 0x02, ++ /* Panel driving setting */ ++ -1, 0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11, ++ /* Frame rate & inv. */ ++ -1, 0xC5, 0x03, ++ /* Pixel format */ ++ -1, 0x3A, 0x55, ++ /* Gamma */ ++ -1, 0xC8, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16, ++ 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00, ++ /* DISP_ON */ ++ -1, 0x29, ++ -3 ++}; ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* column address */ ++ write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff); ++ ++ /* row adress */ ++ write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff); ++ ++ /* memory write */ ++ write_reg(par, 0x2c); ++} ++ ++#define HFLIP 0x01 ++#define VFLIP 0x02 ++#define ROWxCOL 0x20 ++static int set_var(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ switch (par->info->var.rotate) { ++ case 270: ++ write_reg(par, 0x36, ROWxCOL | HFLIP | VFLIP | (par->bgr << 3)); ++ break; ++ case 180: ++ write_reg(par, 0x36, VFLIP | (par->bgr << 3)); ++ break; ++ case 90: ++ write_reg(par, 0x36, ROWxCOL | (par->bgr << 3)); ++ break; ++ default: ++ write_reg(par, 0x36, HFLIP | (par->bgr << 3)); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9481", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9481"); ++MODULE_ALIAS("platform:ili9481"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9481 LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9486.c b/drivers/video/fbtft/fb_ili9486.c +new file mode 100644 +index 0000000..95b8999 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9486.c +@@ -0,0 +1,121 @@ ++/* ++ * FB driver for the ILI9486 LCD Controller ++ * ++ * Copyright (C) 2014 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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 = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9486", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9486"); ++MODULE_ALIAS("platform:ili9486"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_pcd8544.c b/drivers/video/fbtft/fb_pcd8544.c +new file mode 100644 +index 0000000..678ab8e +--- /dev/null ++++ b/drivers/video/fbtft/fb_pcd8544.c +@@ -0,0 +1,177 @@ ++/* ++ * FB driver for the PCD8544 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "philips,pdc8544", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:pdc8544"); ++ ++MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ra8875.c b/drivers/video/fbtft/fb_ra8875.c +new file mode 100644 +index 0000000..c323c06 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ra8875.c +@@ -0,0 +1,331 @@ ++/****************************************************************************** ++ ++ 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, "raio,ra8875", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ra8875"); ++MODULE_ALIAS("platform:ra8875"); ++ ++MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller"); ++MODULE_AUTHOR("Pf@nne"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d02a1.c b/drivers/video/fbtft/fb_s6d02a1.c +new file mode 100644 +index 0000000..e412a42 +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d02a1.c +@@ -0,0 +1,168 @@ ++/* ++ * FB driver for the S6D02A1 LCD Controller ++ * ++ * Based on fb_st7735r.c by Noralf Tronnes ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d02a1", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d02a1"); ++MODULE_ALIAS("platform:s6d02a1"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller"); ++MODULE_AUTHOR("WOLFGANG BUENING"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d1121.c b/drivers/video/fbtft/fb_s6d1121.c +new file mode 100644 +index 0000000..1ef8c1a +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d1121.c +@@ -0,0 +1,208 @@ ++/* ++ * FB driver for the S6D1121 LCD Controller ++ * ++ * Copyright (C) 2013 Roman Rolinsky ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d1121", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d1121"); ++MODULE_ALIAS("platform:s6d1121"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller"); ++MODULE_AUTHOR("Roman Rolinsky"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1289.c b/drivers/video/fbtft/fb_ssd1289.c +new file mode 100644 +index 0000000..ef46fbc +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1289.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the SSD1289 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1289", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1289"); ++MODULE_ALIAS("platform:ssd1289"); ++ ++MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1306.c b/drivers/video/fbtft/fb_ssd1306.c +new file mode 100644 +index 0000000..5ea195b +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1306.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the SSD1306 OLED Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1306", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1306"); ++MODULE_ALIAS("platform:ssd1306"); ++ ++MODULE_DESCRIPTION("SSD1306 OLED Driver"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1331.c b/drivers/video/fbtft/fb_ssd1331.c +new file mode 100644 +index 0000000..da7464f +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1331.c +@@ -0,0 +1,205 @@ ++#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, "solomon,ssd1331", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1331"); ++MODULE_ALIAS("platform:ssd1331"); ++ ++MODULE_DESCRIPTION("SSD1331 OLED Driver"); ++MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1351.c b/drivers/video/fbtft/fb_ssd1351.c +new file mode 100644 +index 0000000..062d986 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1351.c +@@ -0,0 +1,258 @@ ++#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, "solomon,ssd1351", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1351"); ++MODULE_ALIAS("platform:ssd1351"); ++ ++MODULE_DESCRIPTION("SSD1351 OLED Driver"); ++MODULE_AUTHOR("James Davies"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_st7735r.c b/drivers/video/fbtft/fb_st7735r.c +new file mode 100644 +index 0000000..b63aa38 +--- /dev/null ++++ b/drivers/video/fbtft/fb_st7735r.c +@@ -0,0 +1,195 @@ ++/* ++ * FB driver for the ST7735R LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "sitronix,st7735r", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:st7735r"); ++MODULE_ALIAS("platform:st7735r"); ++ ++MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tinylcd.c b/drivers/video/fbtft/fb_tinylcd.c +new file mode 100644 +index 0000000..ca98bfb +--- /dev/null ++++ b/drivers/video/fbtft/fb_tinylcd.c +@@ -0,0 +1,124 @@ ++/* ++ * Custom FB driver for tinylcd.com display ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "neosec,tinylcd", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tinylcd"); ++ ++MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tls8204.c b/drivers/video/fbtft/fb_tls8204.c +new file mode 100644 +index 0000000..8738c7a +--- /dev/null ++++ b/drivers/video/fbtft/fb_tls8204.c +@@ -0,0 +1,176 @@ ++/* ++ * FB driver for the TLS8204 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * Copyright (C) 2014 Michael Hope (adapted for the TLS8204) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "teralane,tls8204", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tls8204"); ++ ++MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller"); ++MODULE_AUTHOR("Michael Hope"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_uc1701.c b/drivers/video/fbtft/fb_uc1701.c +new file mode 100644 +index 0000000..d70ac52 +--- /dev/null ++++ b/drivers/video/fbtft/fb_uc1701.c +@@ -0,0 +1,210 @@ ++/* ++ * FB driver for the UC1701 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2014 Juergen Holzmann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_uc1701" ++#define WIDTH 102 ++#define HEIGHT 64 ++#define PAGES (HEIGHT/8) ++ ++/* 1: Display on/off */ ++#define LCD_DISPLAY_ENABLE 0xAE ++/* 2: display start line set */ ++#define LCD_START_LINE 0x40 ++/* 3: Page address set (lower 4 bits select one of the pages) */ ++#define LCD_PAGE_ADDRESS 0xB0 ++/* 4: column address */ ++#define LCD_COL_ADDRESS 0x10 ++/* 8: select orientation */ ++#define LCD_BOTTOMVIEW 0xA0 ++/* 9: inverted display */ ++#define LCD_DISPLAY_INVERT 0xA6 ++/* 10: show memory content or switch all pixels on */ ++#define LCD_ALL_PIXEL 0xA4 ++/* 11: lcd bias set */ ++#define LCD_BIAS 0xA2 ++/* 14: Reset Controller */ ++#define LCD_RESET_CMD 0xE2 ++/* 15: output mode select (turns display upside-down) */ ++#define LCD_SCAN_DIR 0xC0 ++/* 16: power control set */ ++#define LCD_POWER_CONTROL 0x28 ++/* 17: voltage regulator resistor ratio set */ ++#define LCD_VOLTAGE 0x20 ++/* 18: Volume mode set */ ++#define LCD_VOLUME_MODE 0x81 ++/* 22: NOP command */ ++#define LCD_NO_OP 0xE3 ++/* 25: advanced program control */ ++#define LCD_ADV_PROG_CTRL 0xFA ++/* 25: advanced program control2 */ ++#define LCD_ADV_PROG_CTRL2 0x10 ++#define LCD_TEMPCOMP_HIGH 0x80 ++/* column offset for normal orientation */ ++#define SHIFT_ADDR_NORMAL 0 ++/* column offset for bottom view orientation */ ++#define SHIFT_ADDR_TOPVIEW 30 ++ ++ ++static int init_display(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ /* softreset of LCD */ ++ write_reg(par, LCD_RESET_CMD); ++ mdelay(10); ++ ++ /* set startpoint */ ++ /* LCD_START_LINE | (pos & 0x3F) */ ++ write_reg(par, LCD_START_LINE); ++ ++ /* select orientation BOTTOMVIEW */ ++ write_reg(par, LCD_BOTTOMVIEW | 1); ++ /* output mode select (turns display upside-down) */ ++ write_reg(par, LCD_SCAN_DIR | 0x00); ++ ++ /* Normal Pixel mode */ ++ write_reg(par, LCD_ALL_PIXEL | 0); ++ ++ /* positive display */ ++ write_reg(par, LCD_DISPLAY_INVERT | 0); ++ ++ /* bias 1/9 */ ++ write_reg(par, LCD_BIAS | 0); ++ ++ /* power control mode: all features on */ ++ /* LCD_POWER_CONTROL | (val&0x07) */ ++ write_reg(par, LCD_POWER_CONTROL | 0x07); ++ ++ /* set voltage regulator R/R */ ++ /* LCD_VOLTAGE | (val&0x07) */ ++ write_reg(par, LCD_VOLTAGE | 0x07); ++ ++ /* volume mode set */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_VOLUME_MODE); ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, 0x09); ++ /* ???? */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_NO_OP); ++ ++ /* advanced program control */ ++ write_reg(par, LCD_ADV_PROG_CTRL); ++ write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH); ++ ++ /* enable display */ ++ write_reg(par, LCD_DISPLAY_ENABLE | 1); ++ ++ return 0; ++} ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* goto address */ ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y, i; ++ int ret = 0; ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ for (y = 0; y < PAGES; y++) { ++ buf = par->txbuf.buf; ++ for (x = 0; x < WIDTH; x++) { ++ *buf = 0x00; ++ for (i = 0; i < 8; i++) ++ *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i; ++ buf++; ++ } ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS|(u8)y); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++ gpio_set_value(par->gpio.dc, 1); ++ ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); ++ gpio_set_value(par->gpio.dc, 0); ++ } ++ ++ if (ret < 0) ++ dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret); ++ ++ return ret; ++} ++ ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .write_vmem = write_vmem, ++ }, ++ .backlight = 1, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:uc1701"); ++ ++MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller"); ++MODULE_AUTHOR("Juergen Holzmann"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_upd161704.c b/drivers/video/fbtft/fb_upd161704.c +new file mode 100644 +index 0000000..fff57b3 +--- /dev/null ++++ b/drivers/video/fbtft/fb_upd161704.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "nec,upd161704", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:upd161704"); ++MODULE_ALIAS("platform:upd161704"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_watterott.c b/drivers/video/fbtft/fb_watterott.c +new file mode 100644 +index 0000000..975b579 +--- /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, "watterott,openlcd", &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..873e2c7 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft-core.c +@@ -0,0 +1,1516 @@ ++/* ++ * 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 ++#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 = true; ++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 ret; ++ ++ 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 = devm_gpio_request_one(par->info->device, ++ 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; ++} ++ ++#ifdef CONFIG_OF ++static int fbtft_request_one_gpio(struct fbtft_par *par, ++ const char *name, int index, int *gpiop) ++{ ++ struct device *dev = par->info->device; ++ struct device_node *node = dev->of_node; ++ int gpio, flags, ret = 0; ++ enum of_gpio_flags of_flags; ++ ++ if (of_find_property(node, name, NULL)) { ++ gpio = of_get_named_gpio_flags(node, name, index, &of_flags); ++ if (gpio == -ENOENT) ++ return 0; ++ if (gpio == -EPROBE_DEFER) ++ return gpio; ++ if (gpio < 0) { ++ dev_err(dev, ++ "failed to get '%s' from DT\n", name); ++ return gpio; ++ } ++ ++ /* active low translates to initially low */ ++ flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : ++ GPIOF_OUT_INIT_HIGH; ++ ret = devm_gpio_request_one(dev, gpio, flags, ++ dev->driver->name); ++ if (ret) { ++ dev_err(dev, ++ "gpio_request_one('%s'=%d) failed with %d\n", ++ name, gpio, ret); ++ return ret; ++ } ++ if (gpiop) ++ *gpiop = gpio; ++ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", ++ __func__, name, gpio); ++ } ++ ++ return ret; ++} ++ ++static int fbtft_request_gpios_dt(struct fbtft_par *par) ++{ ++ int i; ++ int ret; ++ ++ if (!par->info->device->of_node) ++ return -EINVAL; ++ ++ ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch); ++ if (ret) ++ return ret; ++ for (i = 0; i < 16; i++) { ++ ret = fbtft_request_one_gpio(par, "db-gpios", i, ++ &par->gpio.db[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "led-gpios", i, ++ &par->gpio.led[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "aux-gpios", i, ++ &par->gpio.aux[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#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->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; ++ int vmem_size, i; ++ 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; ++ ++ if (!pdata) { ++ dev_err(dev, "platform data is missing\n"); ++ return NULL; ++ } ++ ++ /* override driver values? */ ++ if (pdata->fps) ++ fps = pdata->fps; ++ if (pdata->txbuflen) ++ txbuflen = pdata->txbuflen; ++ 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; ++ 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; ++ if (pdata->display.regwidth) ++ display->regwidth = pdata->display.regwidth; ++ ++ display->debug |= debug; ++ fbtft_expand_debug_value(&display->debug); ++ ++ switch (pdata->rotate) { ++ case 90: ++ case 270: ++ width = display->height; ++ height = display->width; ++ break; ++ default: ++ width = display->width; ++ height = display->height; ++ } ++ ++ vmem_size = display->width * display->height * bpp / 8; ++ 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 = pdata->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 = pdata->bgr; ++ par->startbyte = pdata->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) { ++ if (fbtft_gamma_parse_str(par, ++ par->gamma.curves, gamma, strlen(gamma))) ++ goto alloc_fail; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ 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); ++ ++ 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); ++ ret = unregister_framebuffer(fb_info); ++ return ret; ++} ++EXPORT_SYMBOL(fbtft_unregister_framebuffer); ++ ++#ifdef CONFIG_OF ++/** ++ * fbtft_init_display_dt() - Device Tree init_display() function ++ * @par: Driver data ++ * ++ * Return: 0 if successful, negative if error ++ */ ++static int fbtft_init_display_dt(struct fbtft_par *par) ++{ ++ struct device_node *node = par->info->device->of_node; ++ struct property *prop; ++ const __be32 *p; ++ u32 val; ++ int buf[64], i, j; ++ char msg[128]; ++ char str[16]; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ if (!node) ++ return -EINVAL; ++ ++ prop = of_find_property(node, "init", NULL); ++ p = of_prop_next_u32(prop, NULL, &val); ++ if (!p) ++ return -EINVAL; ++ while (p) { ++ if (val & FBTFT_OF_INIT_CMD) { ++ val &= 0xFFFF; ++ i = 0; ++ while (p && !(val & 0xFFFF0000)) { ++ if (i > 63) { ++ dev_err(par->info->device, ++ "%s: Maximum register values exceeded\n", ++ __func__); ++ return -EINVAL; ++ } ++ buf[i++] = val; ++ p = of_prop_next_u32(prop, p, &val); ++ } ++ /* make debug message */ ++ msg[0] = '\0'; ++ for (j = 0; j < i; j++) { ++ snprintf(str, 128, " %02X", buf[j]); ++ strcat(msg, str); ++ } ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: write_register:%s\n", msg); ++ ++ par->fbtftops.write_register(par, i, ++ 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]); ++ } else if (val & FBTFT_OF_INIT_DELAY) { ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: msleep(%u)\n", val & 0xFFFF); ++ msleep(val & 0xFFFF); ++ p = of_prop_next_u32(prop, p, &val); ++ } else { ++ dev_err(par->info->device, "illegal init value 0x%X\n", ++ val); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++/** ++ * 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->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; ++} ++ ++#ifdef CONFIG_OF ++/* returns 0 if the property is not present */ ++static u32 fbtft_of_value(struct device_node *node, const char *propname) ++{ ++ int ret; ++ u32 val = 0; ++ ++ ret = of_property_read_u32(node, propname, &val); ++ if (ret == 0) ++ pr_info("%s: %s = %u\n", __func__, propname, val); ++ ++ return val; ++} ++ ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ struct fbtft_platform_data *pdata; ++ ++ if (!node) { ++ dev_err(dev, "Missing platform data or DT\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ pdata->display.width = fbtft_of_value(node, "width"); ++ pdata->display.height = fbtft_of_value(node, "height"); ++ pdata->display.regwidth = fbtft_of_value(node, "regwidth"); ++ pdata->display.buswidth = fbtft_of_value(node, "buswidth"); ++ pdata->display.backlight = fbtft_of_value(node, "backlight"); ++ pdata->display.bpp = fbtft_of_value(node, "bpp"); ++ pdata->display.debug = fbtft_of_value(node, "debug"); ++ pdata->rotate = fbtft_of_value(node, "rotate"); ++ pdata->bgr = of_property_read_bool(node, "bgr"); ++ pdata->fps = fbtft_of_value(node, "fps"); ++ pdata->txbuflen = fbtft_of_value(node, "txbuflen"); ++ pdata->startbyte = fbtft_of_value(node, "startbyte"); ++ of_property_read_string(node, "gamma", (const char **)&pdata->gamma); ++ ++ if (of_find_property(node, "led-gpios", NULL)) ++ pdata->display.backlight = 1; ++ if (of_find_property(node, "init", NULL)) ++ pdata->display.fbtftops.init_display = fbtft_init_display_dt; ++ pdata->display.fbtftops.request_gpios = fbtft_request_gpios_dt; ++ ++ return pdata; ++} ++#else ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ dev_err(dev, "Missing platform data\n"); ++ return ERR_PTR(-EINVAL); ++} ++#endif ++ ++/** ++ * 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) { ++ pdata = fbtft_probe_dt(dev); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ dev->platform_data = pdata; ++ } ++ ++ info = fbtft_framebuffer_alloc(display, dev); ++ if (!info) ++ return -ENOMEM; ++ ++ par = info->par; ++ par->spi = sdev; ++ par->pdev = pdev; ++ ++ if (display->buswidth == 0) { ++ dev_err(dev, "buswidth is not set\n"); ++ return -EINVAL; ++ } ++ ++ /* 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 */ ++ 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..45f8de3 +--- /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, 0660, show_gamma_curve, store_gamma_curve), ++}; ++ ++ ++void fbtft_expand_debug_value(unsigned long *debug) ++{ ++ switch (*debug & 0b111) { ++ case 1: ++ *debug |= DEBUG_LEVEL_1; ++ break; ++ case 2: ++ *debug |= DEBUG_LEVEL_2; ++ break; ++ case 3: ++ *debug |= DEBUG_LEVEL_3; ++ break; ++ case 4: ++ *debug |= DEBUG_LEVEL_4; ++ break; ++ case 5: ++ *debug |= DEBUG_LEVEL_5; ++ break; ++ case 6: ++ *debug |= DEBUG_LEVEL_6; ++ break; ++ case 7: ++ *debug = 0xFFFFFFFF; ++ break; ++ } ++} ++ ++static ssize_t store_debug(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ int ret; ++ ++ ret = kstrtoul(buf, 10, &par->debug); ++ if (ret) ++ return ret; ++ fbtft_expand_debug_value(&par->debug); ++ ++ return count; ++} ++ ++static ssize_t show_debug(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ ++ return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug); ++} ++ ++static struct device_attribute debug_device_attr = \ ++ __ATTR(debug, 0660, show_debug, store_debug); ++ ++ ++void fbtft_sysfs_init(struct fbtft_par *par) ++{ ++ device_create_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_create_file(par->info->dev, &gamma_device_attrs[0]); ++} ++ ++void fbtft_sysfs_exit(struct fbtft_par *par) ++{ ++ device_remove_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_remove_file(par->info->dev, &gamma_device_attrs[0]); ++} +diff --git a/drivers/video/fbtft/fbtft.h b/drivers/video/fbtft/fbtft.h +new file mode 100644 +index 0000000..0dbf3f9 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft.h +@@ -0,0 +1,447 @@ ++/* ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __LINUX_FBTFT_H ++#define __LINUX_FBTFT_H ++ ++#include ++#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 ++ ++#define FBTFT_OF_INIT_CMD BIT(24) ++#define FBTFT_OF_INIT_DELAY BIT(25) ++ ++/** ++ * struct fbtft_gpio - Structure that holds one pinname to gpio mapping ++ * @name: pinname (reset, dc, etc.) ++ * @gpio: GPIO number ++ * ++ */ ++struct fbtft_gpio { ++ char name[FBTFT_GPIO_NAME_SIZE]; ++ unsigned gpio; ++}; ++ ++struct fbtft_par; ++ ++/** ++ * struct fbtft_ops - FBTFT operations structure ++ * @write: Writes to interface bus ++ * @read: Reads from interface bus ++ * @write_vmem: Writes video memory to display ++ * @write_reg: Writes to controller register ++ * @set_addr_win: Set the GRAM update window ++ * @reset: Reset the LCD controller ++ * @mkdirty: Marks display lines for update ++ * @update_display: Updates the display ++ * @init_display: Initializes the display ++ * @blank: Blank the display (optional) ++ * @request_gpios_match: Do pinname to gpio matching ++ * @request_gpios: Request gpios from the kernel ++ * @free_gpios: Free previously requested gpios ++ * @verify_gpios: Verify that necessary gpios is present (optional) ++ * @register_backlight: Used to register backlight device (optional) ++ * @unregister_backlight: Unregister backlight device (optional) ++ * @set_var: Configure LCD with values from variables like @rotate and @bgr ++ * (optional) ++ * @set_gamma: Set Gamma curve (optional) ++ * ++ * Most of these operations have default functions assigned to them in ++ * fbtft_framebuffer_alloc() ++ */ ++struct fbtft_ops { ++ int (*write)(struct fbtft_par *par, void *buf, size_t len); ++ int (*read)(struct fbtft_par *par, void *buf, size_t len); ++ int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len); ++ void (*write_register)(struct fbtft_par *par, int len, ...); ++ ++ void (*set_addr_win)(struct fbtft_par *par, ++ int xs, int ys, int xe, int ye); ++ void (*reset)(struct fbtft_par *par); ++ void (*mkdirty)(struct fb_info *info, int from, int to); ++ void (*update_display)(struct fbtft_par *par, ++ unsigned start_line, unsigned end_line); ++ int (*init_display)(struct fbtft_par *par); ++ int (*blank)(struct fbtft_par *par, bool on); ++ ++ unsigned long (*request_gpios_match)(struct fbtft_par *par, ++ const struct fbtft_gpio *gpio); ++ int (*request_gpios)(struct fbtft_par *par); ++ int (*verify_gpios)(struct fbtft_par *par); ++ ++ void (*register_backlight)(struct fbtft_par *par); ++ void (*unregister_backlight)(struct fbtft_par *par); ++ ++ int (*set_var)(struct fbtft_par *par); ++ int (*set_gamma)(struct fbtft_par *par, unsigned long *curves); ++}; ++ ++/** ++ * struct fbtft_display - Describes the display properties ++ * @width: Width of display in pixels ++ * @height: Height of display in pixels ++ * @regwidth: LCD Controller Register width in bits ++ * @buswidth: Display interface bus width in bits ++ * @backlight: Backlight type. ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @bpp: Bits per pixel ++ * @fps: Frames per second ++ * @txbuflen: Size of transmit buffer ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma: String representation of Gamma curve(s) ++ * @gamma_num: Number of Gamma curves ++ * @gamma_len: Number of values per Gamma curve ++ * @debug: Initial debug value ++ * ++ * This structure is not stored by FBTFT except for init_sequence. ++ */ ++struct fbtft_display { ++ unsigned width; ++ unsigned height; ++ unsigned regwidth; ++ unsigned buswidth; ++ unsigned backlight; ++ struct fbtft_ops fbtftops; ++ unsigned bpp; ++ unsigned fps; ++ int txbuflen; ++ int *init_sequence; ++ char *gamma; ++ int gamma_num; ++ int gamma_len; ++ unsigned long debug; ++}; ++ ++/** ++ * struct fbtft_platform_data - Passes display specific data to the driver ++ * @display: Display properties ++ * @gpios: Pointer to an array of piname to gpio mappings ++ * @rotate: Display rotation angle ++ * @bgr: LCD Controller BGR bit ++ * @fps: Frames per second (this will go away, use @fps in @fbtft_display) ++ * @txbuflen: Size of transmit buffer ++ * @startbyte: When set, enables use of Startbyte in transfers ++ * @gamma: String representation of Gamma curve(s) ++ * @extra: A way to pass extra info ++ */ ++struct fbtft_platform_data { ++ struct fbtft_display display; ++ const struct fbtft_gpio *gpios; ++ unsigned rotate; ++ bool bgr; ++ unsigned fps; ++ int txbuflen; ++ u8 startbyte; ++ char *gamma; ++ void *extra; ++}; ++ ++/** ++ * struct fbtft_par - Main FBTFT data structure ++ * ++ * This structure holds all relevant data to operate the display ++ * ++ * See sourcefile for documentation since nested structs is not ++ * supported by kernel-doc. ++ * ++ */ ++/* @spi: Set if it is a SPI device ++ * @pdev: Set if it is a platform device ++ * @info: Pointer to framebuffer fb_info structure ++ * @pdata: Pointer to platform data ++ * @ssbuf: Not used ++ * @pseudo_palette: Used by fb_set_colreg() ++ * @txbuf.buf: Transmit buffer ++ * @txbuf.len: Transmit buffer length ++ * @buf: Small buffer used when writing init data over SPI ++ * @startbyte: Used by some controllers when in SPI mode. ++ * Format: 6 bit Device id + RS bit + RW bit ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @dirty_lock: Protects dirty_lines_start and dirty_lines_end ++ * @dirty_lines_start: Where to begin updating display ++ * @dirty_lines_end: Where to end updating display ++ * @gpio.reset: GPIO used to reset display ++ * @gpio.dc: Data/Command signal, also known as RS ++ * @gpio.rd: Read latching signal ++ * @gpio.wr: Write latching signal ++ * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch ++ * @gpio.cs: LCD Chip Select with parallel interface bus ++ * @gpio.db[16]: Parallel databus ++ * @gpio.led[16]: Led control signals ++ * @gpio.aux[16]: Auxillary signals, not used by core ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma.lock: Mutex for Gamma curve locking ++ * @gamma.curves: Pointer to Gamma curve array ++ * @gamma.num_values: Number of values per Gamma curve ++ * @gamma.num_curves: Number of Gamma curves ++ * @debug: Pointer to debug value ++ * @current_debug: ++ * @first_update_done: Used to only time the first display update ++ * @update_time: Used to calculate 'fps' in debug output ++ * @bgr: BGR mode/\n ++ * @extra: Extra info needed by driver ++ */ ++struct fbtft_par { ++ struct spi_device *spi; ++ struct platform_device *pdev; ++ struct fb_info *info; ++ struct fbtft_platform_data *pdata; ++ u16 *ssbuf; ++ u32 pseudo_palette[16]; ++ struct { ++ void *buf; ++ dma_addr_t dma; ++ size_t len; ++ } txbuf; ++ u8 *buf; ++ u8 startbyte; ++ struct fbtft_ops fbtftops; ++ spinlock_t dirty_lock; ++ unsigned dirty_lines_start; ++ unsigned dirty_lines_end; ++ struct { ++ int reset; ++ int dc; ++ int rd; ++ int wr; ++ int latch; ++ int cs; ++ int db[16]; ++ int led[16]; ++ int aux[16]; ++ } gpio; ++ int *init_sequence; ++ struct { ++ struct mutex lock; ++ unsigned long *curves; ++ int num_values; ++ int num_curves; ++ } gamma; ++ unsigned long debug; ++ bool first_update_done; ++ struct timespec update_time; ++ bool bgr; ++ void *extra; ++}; ++ ++#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) ++ ++#define write_reg(par, ...) \ ++do { \ ++ par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \ ++} while (0) ++ ++/* fbtft-core.c */ ++extern void fbtft_dbg_hex(const struct device *dev, ++ int groupsize, void *buf, size_t len, const char *fmt, ...); ++extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, ++ struct device *dev); ++extern void fbtft_framebuffer_release(struct fb_info *info); ++extern int fbtft_register_framebuffer(struct fb_info *fb_info); ++extern int fbtft_unregister_framebuffer(struct fb_info *fb_info); ++extern void fbtft_register_backlight(struct fbtft_par *par); ++extern void fbtft_unregister_backlight(struct fbtft_par *par); ++extern int fbtft_init_display(struct fbtft_par *par); ++extern int fbtft_probe_common(struct fbtft_display *display, ++ struct spi_device *sdev, struct platform_device *pdev); ++extern int fbtft_remove_common(struct device *dev, struct fb_info *info); ++ ++/* fbtft-io.c */ ++extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_spi_emulate_9(struct fbtft_par *par, ++ void *buf, size_t len); ++extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, ++ void *buf, size_t len); ++ ++/* fbtft-bus.c */ ++extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len); ++extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...); ++ ++ ++#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \ ++ \ ++static int fbtft_driver_probe_spi(struct spi_device *spi) \ ++{ \ ++ return fbtft_probe_common(_display, spi, NULL); \ ++} \ ++ \ ++static int fbtft_driver_remove_spi(struct spi_device *spi) \ ++{ \ ++ struct fb_info *info = spi_get_drvdata(spi); \ ++ \ ++ return fbtft_remove_common(&spi->dev, info); \ ++} \ ++ \ ++static int fbtft_driver_probe_pdev(struct platform_device *pdev) \ ++{ \ ++ return fbtft_probe_common(_display, NULL, pdev); \ ++} \ ++ \ ++static int fbtft_driver_remove_pdev(struct platform_device *pdev) \ ++{ \ ++ struct fb_info *info = platform_get_drvdata(pdev); \ ++ \ ++ return fbtft_remove_common(&pdev->dev, info); \ ++} \ ++ \ ++static const struct of_device_id dt_ids[] = { \ ++ { .compatible = _compatible }, \ ++ {}, \ ++}; \ ++ \ ++MODULE_DEVICE_TABLE(of, dt_ids); \ ++ \ ++ \ ++static struct spi_driver fbtft_driver_spi_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_spi, \ ++ .remove = fbtft_driver_remove_spi, \ ++}; \ ++ \ ++static struct platform_driver fbtft_driver_platform_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_pdev, \ ++ .remove = fbtft_driver_remove_pdev, \ ++}; \ ++ \ ++static int __init fbtft_driver_module_init(void) \ ++{ \ ++ int ret; \ ++ \ ++ ret = spi_register_driver(&fbtft_driver_spi_driver); \ ++ if (ret < 0) \ ++ return ret; \ ++ return platform_driver_register(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++static void __exit fbtft_driver_module_exit(void) \ ++{ \ ++ spi_unregister_driver(&fbtft_driver_spi_driver); \ ++ platform_driver_unregister(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++module_init(fbtft_driver_module_init); \ ++module_exit(fbtft_driver_module_exit); ++ ++ ++/* Debug macros */ ++ ++/* shorthand debug levels */ ++#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS ++#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE) ++#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS) ++#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK) ++#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY) ++#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5) ++#define DEBUG_LEVEL_7 0xFFFFFFFF ++ ++#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3) ++#define DEBUG_TIME_FIRST_UPDATE (1<<4) ++#define DEBUG_TIME_EACH_UPDATE (1<<5) ++#define DEBUG_DEFERRED_IO (1<<6) ++#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7) ++ ++/* fbops */ ++#define DEBUG_FB_READ (1<<8) ++#define DEBUG_FB_WRITE (1<<9) ++#define DEBUG_FB_FILLRECT (1<<10) ++#define DEBUG_FB_COPYAREA (1<<11) ++#define DEBUG_FB_IMAGEBLIT (1<<12) ++#define DEBUG_FB_SETCOLREG (1<<13) ++#define DEBUG_FB_BLANK (1<<14) ++ ++#define DEBUG_SYSFS (1<<16) ++ ++/* fbtftops */ ++#define DEBUG_BACKLIGHT (1<<17) ++#define DEBUG_READ (1<<18) ++#define DEBUG_WRITE (1<<19) ++#define DEBUG_WRITE_VMEM (1<<20) ++#define DEBUG_WRITE_REGISTER (1<<21) ++#define DEBUG_SET_ADDR_WIN (1<<22) ++#define DEBUG_RESET (1<<23) ++#define DEBUG_MKDIRTY (1<<24) ++#define DEBUG_UPDATE_DISPLAY (1<<25) ++#define DEBUG_INIT_DISPLAY (1<<26) ++#define DEBUG_BLANK (1<<27) ++#define DEBUG_REQUEST_GPIOS (1<<28) ++#define DEBUG_FREE_GPIOS (1<<29) ++#define DEBUG_REQUEST_GPIOS_MATCH (1<<30) ++#define DEBUG_VERIFY_GPIOS (1<<31) ++ ++ ++#define fbtft_init_dbg(dev, format, arg...) \ ++do { \ ++ if (unlikely((dev)->platform_data && \ ++ (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg(level, par, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(par->info->device, format, ##arg); \ ++} while (0) ++ ++#define fbtft_dev_dbg(level, par, dev, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \ ++} while (0) ++ ++#endif /* __LINUX_FBTFT_H */ +diff --git a/drivers/video/fbtft/fbtft_device.c b/drivers/video/fbtft/fbtft_device.c +new file mode 100644 +index 0000000..b9f4c30 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft_device.c +@@ -0,0 +1,1444 @@ ++/* ++ * ++ * 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 }; ++ ++static int waveshare32b_init_sequence[] = { ++ -1,0xCB,0x39,0x2C,0x00,0x34,0x02,-1,0xCF,0x00,0xC1,0x30, ++ -1,0xE8,0x85,0x00,0x78,-1,0xEA,0x00,0x00,-1,0xED,0x64,0x03,0x12,0x81, ++ -1,0xF7,0x20,-1,0xC0,0x23,-1,0xC1,0x10,-1,0xC5,0x3e,0x28,-1,0xC7,0x86, ++ -1,0x36,0x28,-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,120,-1,0x29,-1,0x2c,-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 = "adafruit28", ++ .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 = "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 = "agm1264k-fl", ++ .pdev = &(struct platform_device) { ++ .name = "fb_agm1264k-fl", ++ .id = 0, ++ .dev = { ++ .release = fbtft_device_pdev_release, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = FBTFT_ONBOARD_BACKLIGHT, ++ }, ++ .gpios = (const struct fbtft_gpio []) { ++ {}, ++ }, ++ }, ++ } ++ } ++ }, { ++ .name = "dogs102", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_uc1701", ++ .max_speed_hz = 8000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 13 }, ++ { "dc", 6 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = "ili9481", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 32000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .regwidth = 16, ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 25 }, ++ { "dc", 24 }, ++ { "led", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = { ++ .regwidth = 16, ++ .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 = "tontec35_9481", /* boards before 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "led_", 18 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .name = "tontec35_9486", /* boards after 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9486", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "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 = "waveshare32b", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9340", ++ .max_speed_hz = 48000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ .init_sequence = waveshare32b_init_sequence, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 27 }, ++ { "dc", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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/sun7i-default/fbtft_for_older.patch b/patch/kernel/sun7i-default/fbtft_for_older.patch new file mode 100644 index 000000000..55e63c1d9 --- /dev/null +++ b/patch/kernel/sun7i-default/fbtft_for_older.patch @@ -0,0 +1,9505 @@ +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/sun7i-dev/packaging-next.patch b/patch/kernel/sun7i-dev/packaging-next.patch new file mode 100644 index 000000000..731b65d62 --- /dev/null +++ b/patch/kernel/sun7i-dev/packaging-next.patch @@ -0,0 +1,143 @@ +diff --git a/scripts/package/builddeb b/scripts/package/builddeb +index 88dbf23..fc023a9 100755 +--- a/scripts/package/builddeb ++++ b/scripts/package/builddeb +@@ -80,11 +80,13 @@ tmpdir="$objtree/debian/tmp" + fwdir="$objtree/debian/fwtmp" + kernel_headers_dir="$objtree/debian/hdrtmp" + libc_headers_dir="$objtree/debian/headertmp" ++dtb_dir="$objtree/debian/dtbtmp" + 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-next"$LOCALVERSION" ++fwpackagename=linux-firmware-image-next"$LOCALVERSION" ++kernel_headers_packagename=linux-headers-next"$LOCALVERSION" ++dtb_packagename=linux-dtb-next"$LOCALVERSION" ++libc_headers_packagename=linux-libc-dev-next"$LOCALVERSION" + dbg_packagename=$packagename-dbg + + if [ "$ARCH" = "um" ] ; then +@@ -108,6 +110,8 @@ esac + BUILD_DEBUG="$(grep -s '^CONFIG_DEBUG_INFO=y' $KCONFIG_CONFIG || true)" + + # Setup the directory structure +-rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" $objtree/debian/files ++rm -rf "$tmpdir" "$fwdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" "$dtb_dir" $objtree/debian/files ++mkdir -m 755 -p "$dtb_dir/DEBIAN" ++mkdir -p "$dtb_dir/boot/dtb" "$dtb_dir/usr/share/doc/$dtb_packagename" + mkdir -m 755 -p "$tmpdir/DEBIAN" + mkdir -p "$tmpdir/lib" "$tmpdir/boot" +@@ -165,6 +171,11 @@ if grep -q '^CONFIG_MODULES=y' $KCONFIG_CONFIG ; then + fi + fi + ++if grep -q '^CONFIG_OF=y' $KCONFIG_CONFIG ; then ++ #mkdir -p "$tmpdir/boot/dtb" ++ INSTALL_DTBS_PATH="$dtb_dir/boot/dtb" $MAKE KBUILD_SRC= dtbs_install ++fi ++ + if [ "$ARCH" != "um" ]; then + $MAKE headers_check KBUILD_SRC= + $MAKE headers_install KBUILD_SRC= INSTALL_HDR_PATH="$libc_headers_dir/usr" +@@ -177,7 +188,7 @@ fi + # so do we; recent versions of dracut and initramfs-tools will obey this. + debhookdir=${KDEB_HOOKDIR:-/etc/kernel} + if grep -q '^CONFIG_BLK_DEV_INITRD=y' $KCONFIG_CONFIG; then +- want_initrd=Yes ++ want_initrd=Yes + else + want_initrd=No + fi +@@ -189,9 +200,11 @@ for script in postinst postrm preinst prerm ; do + set -e + + # Pass maintainer script parameters to hook scripts ++ + export DEB_MAINT_PARAMS="\$*" + + # Tell initramfs builder whether it's wanted ++ + export INITRD=$want_initrd + + test -d $debhookdir/$script.d && run-parts --arg="$version" --arg="/$installed_image_path" $debhookdir/$script.d +@@ -200,6 +213,29 @@ EOF + chmod 755 "$tmpdir/DEBIAN/$script" + done + ++## ++## Create sym link to kernel image ++## ++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 ++touch /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 + email=$DEBEMAIL +@@ -306,6 +342,12 @@ fi + (cd $objtree; find arch/$SRCARCH/include Module.symvers include scripts -type f) >> "$objtree/debian/hdrobjfiles" + destdir=$kernel_headers_dir/usr/src/linux-headers-$version + mkdir -p "$destdir" ++######################## headers patch ++ZACNI=$(pwd) ++cd $destdir ++patch -p1 < /tmp/headers-debian-byteshift.patch ++cd $ZACNI ++######################## headers patch + (cd $srctree; tar -c -f - -T -) < "$objtree/debian/hdrsrcfiles" | (cd $destdir; tar -xf -) + (cd $objtree; tar -c -f - -T -) < "$objtree/debian/hdrobjfiles" | (cd $destdir; tar -xf -) + (cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be +@@ -315,7 +357,7 @@ rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles" + cat <> debian/control + + Package: $kernel_headers_packagename +-Provides: linux-headers, linux-headers-2.6 ++Provides: linux-headers + Architecture: any + Description: Linux kernel headers for $KERNELRELEASE on \${kernel:debarch} + This package provides kernel header files for $KERNELRELEASE on \${kernel:debarch} +@@ -341,6 +383,16 @@ fi + + cat <> debian/control + ++Package: $dtb_packagename ++Architecture: any ++Description: Linux DTB, version $version ++ This package contains device blobs from the Linux kernel, version $version. ++EOF ++ ++create_package "$dtb_packagename" "$dtb_dir" ++ ++cat <> debian/control ++ + Package: $libc_headers_packagename + Section: devel + Provides: linux-kernel-headers +@@ -352,7 +404,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 new file mode 100644 index 000000000..55e63c1d9 --- /dev/null +++ b/patch/kernel/sunxi-default/fbtft_for_older.patch @@ -0,0 +1,9505 @@ +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/udoo-default/fbtft_drivers.patch b/patch/kernel/udoo-default/fbtft_drivers.patch new file mode 100644 index 000000000..8ecb55230 --- /dev/null +++ b/patch/kernel/udoo-default/fbtft_drivers.patch @@ -0,0 +1,12012 @@ +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 03bed11..48f1f71 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/host1x/Kconfig" +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 07905d0..14810e4 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-$(CONFIG_HDMI) += hdmi.o + obj-y += fb_notify.o +diff --git a/drivers/video/fbtft/Kconfig b/drivers/video/fbtft/Kconfig +new file mode 100644 +index 0000000..995a910 +--- /dev/null ++++ b/drivers/video/fbtft/Kconfig +@@ -0,0 +1,169 @@ ++menuconfig FB_TFT ++ tristate "Support for small TFT LCD display modules" ++ depends on FB && SPI && GPIOLIB ++ select FB_SYS_FILLRECT ++ select FB_SYS_COPYAREA ++ select FB_SYS_IMAGEBLIT ++ select FB_SYS_FOPS ++ select FB_DEFERRED_IO ++ select FB_BACKLIGHT ++ ++config FB_TFT_AGM1264K_FL ++ tristate "FB driver for the AGM1264K-FL LCD display" ++ depends on FB_TFT ++ help ++ Framebuffer support for the AGM1264K-FL LCD display (two Samsung KS0108 compatable chips) ++ ++config FB_TFT_BD663474 ++ tristate "FB driver for the BD663474 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for BD663474 ++ ++config FB_TFT_HX8340BN ++ tristate "FB driver for the HX8340BN LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8340BN ++ ++config FB_TFT_HX8347D ++ tristate "FB driver for the HX8347D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8347D ++ ++config FB_TFT_HX8353D ++ tristate "FB driver for the HX8353D LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for HX8353D ++ ++config FB_TFT_ILI9320 ++ tristate "FB driver for the ILI9320 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9320 ++ ++config FB_TFT_ILI9325 ++ tristate "FB driver for the ILI9325 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9325 ++ ++config FB_TFT_ILI9340 ++ tristate "FB driver for the ILI9340 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9340 ++ ++config FB_TFT_ILI9341 ++ tristate "FB driver for the ILI9341 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9341 ++ ++config FB_TFT_ILI9481 ++ tristate "FB driver for the ILI9481 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9481 ++ ++config FB_TFT_ILI9486 ++ tristate "FB driver for the ILI9486 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ILI9486 ++ ++config FB_TFT_PCD8544 ++ tristate "FB driver for the PCD8544 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for PCD8544 ++ ++config FB_TFT_RA8875 ++ tristate "FB driver for the RA8875 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for RA8875 ++ ++config FB_TFT_S6D02A1 ++ tristate "FB driver for the S6D02A1 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D02A1 ++ ++config FB_TFT_S6D1121 ++ tristate "FB driver for the S6D1211 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for S6D1121 ++ ++config FB_TFT_SSD1289 ++ tristate "FB driver for the SSD1289 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1289 ++ ++config FB_TFT_SSD1306 ++ tristate "FB driver for the SSD1306 OLED Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1306 ++ ++config FB_TFT_SSD1331 ++ tristate "FB driver for the SSD1331 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1331 ++ ++config FB_TFT_SSD1351 ++ tristate "FB driver for the SSD1351 LCD Controller" ++ depends on FB_TFT ++ help ++ Framebuffer support for SSD1351 ++ ++config FB_TFT_ST7735R ++ tristate "FB driver for the ST7735R LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for ST7735R ++ ++config FB_TFT_TINYLCD ++ tristate "FB driver for tinylcd.com display" ++ depends on FB_TFT ++ help ++ Custom Framebuffer support for tinylcd.com display ++ ++config FB_TFT_TLS8204 ++ tristate "FB driver for the TLS8204 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TLS8204 ++ ++config FB_TFT_UC1701 ++ tristate "FB driver for the UC1701 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for UC1701 ++ ++config FB_TFT_UPD161704 ++ tristate "FB driver for the uPD161704 LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for uPD161704 ++ ++config FB_TFT_WATTEROTT ++ tristate "FB driver for the WATTEROTT LCD Controller" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for WATTEROTT ++ ++config FB_FLEX ++ tristate "Generic FB driver for TFT LCD displays" ++ depends on FB_TFT ++ help ++ Generic Framebuffer support for TFT LCD displays. ++ ++config FB_TFT_FBTFT_DEVICE ++ tristate "Module to for adding FBTFT devices" ++ depends on FB_TFT +diff --git a/drivers/video/fbtft/Makefile b/drivers/video/fbtft/Makefile +new file mode 100644 +index 0000000..71c755d +--- /dev/null ++++ b/drivers/video/fbtft/Makefile +@@ -0,0 +1,60 @@ ++ifneq ($(KERNELRELEASE),) ++# kbuild part of makefile ++ ++# Optionally, include config file to allow out of tree kernel modules build ++-include $(src)/.config ++ ++# Core module ++obj-$(CONFIG_FB_TFT) += fbtft.o ++fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o ++ ++# drivers ++obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o ++obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o ++obj-$(CONFIG_FB_TFT_HX8340BN) += fb_hx8340bn.o ++obj-$(CONFIG_FB_TFT_HX8347D) += fb_hx8347d.o ++obj-$(CONFIG_FB_TFT_HX8353D) += fb_hx8353d.o ++obj-$(CONFIG_FB_TFT_ILI9320) += fb_ili9320.o ++obj-$(CONFIG_FB_TFT_ILI9325) += fb_ili9325.o ++obj-$(CONFIG_FB_TFT_ILI9340) += fb_ili9340.o ++obj-$(CONFIG_FB_TFT_ILI9341) += fb_ili9341.o ++obj-$(CONFIG_FB_TFT_ILI9481) += fb_ili9481.o ++obj-$(CONFIG_FB_TFT_ILI9486) += fb_ili9486.o ++obj-$(CONFIG_FB_TFT_PCD8544) += fb_pcd8544.o ++obj-$(CONFIG_FB_TFT_RA8875) += fb_ra8875.o ++obj-$(CONFIG_FB_TFT_S6D02A1) += fb_s6d02a1.o ++obj-$(CONFIG_FB_TFT_S6D1121) += fb_s6d1121.o ++obj-$(CONFIG_FB_TFT_SSD1289) += fb_ssd1289.o ++obj-$(CONFIG_FB_TFT_SSD1306) += fb_ssd1306.o ++obj-$(CONFIG_FB_TFT_SSD1331) += fb_ssd1331.o ++obj-$(CONFIG_FB_TFT_SSD1351) += fb_ssd1351.o ++obj-$(CONFIG_FB_TFT_ST7735R) += fb_st7735r.o ++obj-$(CONFIG_FB_TFT_TINYLCD) += fb_tinylcd.o ++obj-$(CONFIG_FB_TFT_TLS8204) += fb_tls8204.o ++obj-$(CONFIG_FB_TFT_UC1701) += fb_uc1701.o ++obj-$(CONFIG_FB_TFT_UPD161704) += fb_upd161704.o ++obj-$(CONFIG_FB_TFT_WATTEROTT) += fb_watterott.o ++obj-$(CONFIG_FB_FLEX) += flexfb.o ++ ++# Device modules ++obj-$(CONFIG_FB_TFT_FBTFT_DEVICE) += fbtft_device.o ++ ++else ++# normal makefile ++KDIR ?= /lib/modules/`uname -r`/build ++ ++default: .config ++ $(MAKE) -C $(KDIR) M=$$PWD modules ++ ++.config: ++ grep config Kconfig | cut -d' ' -f2 | sed 's@^@CONFIG_@; s@$$@=m@' > .config ++ ++install: ++ $(MAKE) -C $(KDIR) M=$$PWD modules_install ++ ++ ++clean: ++ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions \ ++ modules.order Module.symvers ++ ++endif +diff --git a/drivers/video/fbtft/README b/drivers/video/fbtft/README +new file mode 100644 +index 0000000..9bebc98 +--- /dev/null ++++ b/drivers/video/fbtft/README +@@ -0,0 +1,37 @@ ++ FBTFT ++========= ++ ++2015-01-19 ++The FBTFT drivers are now in the Linux kernel staging tree: https://git.kernel.org/cgit/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fbtft?h=staging-testing ++Development in this github repo has ceased. ++ ++ ++Linux Framebuffer drivers for small TFT LCD display modules. ++The module 'fbtft' makes writing drivers for some of these displays very easy. ++ ++Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. ++ ++INSTALLATION ++ Download kernel sources ++ ++ From Linux 3.15 ++ cd drivers/video/fbdev ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" ++ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ ++ ++ Before Linux 3.15 ++ cd drivers/video ++ git clone https://github.com/notro/fbtft.git ++ ++ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" ++ Add to drivers/video/Makefile: obj-y += fbtft/ ++ ++ Enable driver(s) in menuconfig and build the kernel ++ ++ ++See wiki for more information: https://github.com/notro/fbtft/wiki ++ ++ ++Source: https://github.com/notro/fbtft/ +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +new file mode 100644 +index 0000000..621497f +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28a-overlay.dts +@@ -0,0 +1,87 @@ ++/* ++ * Device Tree overlay for HY28A display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28a_pins: hy28a_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28a: hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28a_pins>; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts: hy28a-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28a>,"spi-max-frequency:0"; ++ rotate = <&hy28a>,"rotate:0"; ++ fps = <&hy28a>,"fps:0"; ++ debug = <&hy28a>,"debug:0"; ++ xohms = <&hy28a_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28a>,"reset-gpios:4", ++ <&hy28a_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28a>,"led-gpios:4", ++ <&hy28a_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +new file mode 100644 +index 0000000..f774c4a +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/hy28b-overlay.dts +@@ -0,0 +1,142 @@ ++/* ++ * Device Tree overlay for HY28b display shield by Texy ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ hy28b_pins: hy28b_pins { ++ brcm,pins = <17 25 18>; ++ brcm,function = <0 1 1>; /* in out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hy28b: hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hy28b_pins>; ++ ++ spi-max-frequency = <48000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts: hy28b-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&hy28b>,"spi-max-frequency:0"; ++ rotate = <&hy28b>,"rotate:0"; ++ fps = <&hy28b>,"fps:0"; ++ debug = <&hy28b>,"debug:0"; ++ xohms = <&hy28b_ts>,"ti,x-plate-ohms;0"; ++ resetgpio = <&hy28b>,"reset-gpios:4", ++ <&hy28b_pins>, "brcm,pins:1"; ++ ledgpio = <&hy28b>,"led-gpios:4", ++ <&hy28b_pins>, "brcm,pins:2"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +new file mode 100644 +index 0000000..c06fe12 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/mz61581-overlay.dts +@@ -0,0 +1,109 @@ ++/* ++ * Device Tree overlay for MZ61581-PI-EXT 2014.12.28 by Tontec ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ mz61581_pins: mz61581_pins { ++ brcm,pins = <4 15 18 25>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ mz61581: mz61581@0{ ++ compatible = "samsung,s6d02a1"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mz61581_pins>; ++ ++ spi-max-frequency = <128000000>; ++ spi-cpol; ++ spi-cpha; ++ ++ width = <320>; ++ height = <480>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ ++ reset-gpios = <&gpio 15 0>; ++ dc-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 0>; ++ ++ init = <0x10000b0 00 ++ 0x1000011 ++ 0x20000ff ++ 0x10000b3 0x02 0x00 0x00 0x00 ++ 0x10000c0 0x13 0x3b 0x00 0x02 0x00 0x01 0x00 0x43 ++ 0x10000c1 0x08 0x16 0x08 0x08 ++ 0x10000c4 0x11 0x07 0x03 0x03 ++ 0x10000c6 0x00 ++ 0x10000c8 0x03 0x03 0x13 0x5c 0x03 0x07 0x14 0x08 0x00 0x21 0x08 0x14 0x07 0x53 0x0c 0x13 0x03 0x03 0x21 0x00 ++ 0x1000035 0x00 ++ 0x1000036 0xa0 ++ 0x100003a 0x55 ++ 0x1000044 0x00 0x01 ++ 0x10000d0 0x07 0x07 0x1d 0x03 ++ 0x10000d1 0x03 0x30 0x10 ++ 0x10000d2 0x03 0x14 0x04 ++ 0x1000029 ++ 0x100002c>; ++ ++ /* This is a workaround to make sure the init sequence slows down and doesn't fail */ ++ debug = <3>; ++ }; ++ ++ mz61581_ts: mz61581_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <4 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 4 0>; ++ ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&mz61581>, "spi-max-frequency:0"; ++ rotate = <&mz61581>, "rotate:0"; ++ fps = <&mz61581>, "fps:0"; ++ debug = <&mz61581>, "debug:0"; ++ xohms = <&mz61581_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +new file mode 100644 +index 0000000..8cd6a95 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/piscreen-overlay.dts +@@ -0,0 +1,94 @@ ++/* ++ * Device Tree overlay for PiScreen 3.5" display shield by Ozzmaker ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ piscreen_pins: piscreen_pins { ++ brcm,pins = <17 25 24 22>; ++ brcm,function = <0 1 1 1>; /* in out out out */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ piscreen: piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&piscreen_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ regwidth = <16>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ debug = <0>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000ff ++ 0x100003a 0x55 ++ 0x1000036 0x28 ++ 0x10000c2 0x44 ++ 0x10000c5 0x00 0x00 0x00 0x00 ++ 0x10000e0 0x0f 0x1f 0x1c 0x0c 0x0f 0x08 0x48 0x98 0x37 0x0a 0x13 0x04 0x11 0x0d 0x00 ++ 0x10000e1 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000e2 0x0f 0x32 0x2e 0x0b 0x0d 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0x1000029>; ++ }; ++ ++ piscreen-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <17 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&piscreen>,"spi-max-frequency:0"; ++ rotate = <&piscreen>,"rotate:0"; ++ fps = <&piscreen>,"fps:0"; ++ debug = <&piscreen>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +new file mode 100644 +index 0000000..e8a9365 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/pitft28-resistive-overlay.dts +@@ -0,0 +1,115 @@ ++/* ++ * Device Tree overlay for pitft resistive by Adafruit ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ pitft_pins: pitft_pins { ++ brcm,pins = <24 25>; ++ brcm,function = <0 1>; /* in out */ ++ brcm,pull = <2 0>; /* pullup none */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pitft: pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pitft_pins>; ++ ++ spi-max-frequency = <16000000>; ++ rotate = <90>; ++ fps = <25>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ debug = <0>; ++ }; ++ ++ pitft_ts@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "st,stmpe610"; ++ reg = <1>; ++ ++ spi-max-frequency = <500000>; ++ irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */ ++ interrupts = <24 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ ++ stmpe_gpio: stmpe_gpio { ++ #gpio-cells = <2>; ++ compatible = "st,stmpe-gpio"; ++ /* ++ * only GPIO2 is wired/available ++ * and it is wired to the backlight ++ */ ++ st,norequest-mask = <0x7b>; ++ }; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target-path = "/soc"; ++ __overlay__ { ++ backlight { ++ compatible = "gpio-backlight"; ++ gpios = <&stmpe_gpio 2 0>; ++ default-on; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&pitft>,"spi-max-frequency:0"; ++ rotate = <&pitft>,"rotate:0"; ++ fps = <&pitft>,"fps:0"; ++ debug = <&pitft>,"debug:0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +new file mode 100644 +index 0000000..0578810 +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/rpi-display-overlay.dts +@@ -0,0 +1,81 @@ ++/* ++ * Device Tree overlay for rpi-display by Watterott ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ rpi_display_pins: rpi_display_pins { ++ brcm,pins = <18 23 24 25>; ++ brcm,function = <1 1 1 0>; /* out out out in */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ rpidisplay: rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&rpi_display_pins>; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpidisplay_ts: rpi-display-ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <25 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ __overrides__ { ++ speed = <&rpidisplay>,"spi-max-frequency:0"; ++ rotate = <&rpidisplay>,"rotate:0"; ++ fps = <&rpidisplay>,"fps:0"; ++ debug = <&rpidisplay>,"debug:0"; ++ xohms = <&rpidisplay_ts>,"ti,x-plate-ohms;0"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +new file mode 100644 +index 0000000..881d2eb +--- /dev/null ++++ b/drivers/video/fbtft/dts/overlays/rpi/tinylcd35-overlay.dts +@@ -0,0 +1,181 @@ ++/* ++ * tinylcd 3.5" display ++ * ++ */ ++ ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2835", "brcm,bcm2708", "brcm,bcm2709"; ++ ++ fragment@0 { ++ target = <&spi0>; ++ __overlay__ { ++ status = "okay"; ++ ++ spidev@0{ ++ status = "disabled"; ++ }; ++ ++ spidev@1{ ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&gpio>; ++ __overlay__ { ++ tinylcd35_pins: tinylcd35_pins { ++ brcm,pins = <25 24 18>; ++ brcm,function = <1>; /* out */ ++ }; ++ tinylcd35_ts_pins: tinylcd35_ts_pins { ++ brcm,pins = <5>; ++ brcm,function = <0>; /* in */ ++ }; ++ keypad_pins: keypad_pins { ++ brcm,pins = <4 17 22 23 27>; ++ brcm,function = <0>; /* in */ ++ brcm,pull = <1>; /* down */ ++ }; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&spi0>; ++ __overlay__ { ++ /* needed to avoid dtc warning */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ tinylcd35: tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&tinylcd35_pins>, ++ <&tinylcd35_ts_pins>; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ fps = <20>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ ++ init = <0x10000B0 0x80 ++ 0x10000C0 0x0A 0x0A ++ 0x10000C1 0x01 0x01 ++ 0x10000C2 0x33 ++ 0x10000C5 0x00 0x42 0x80 ++ 0x10000B1 0xD0 0x11 ++ 0x10000B4 0x02 ++ 0x10000B6 0x00 0x22 0x3B ++ 0x10000B7 0x07 ++ 0x1000036 0x58 ++ 0x10000F0 0x36 0xA5 0xD3 ++ 0x10000E5 0x80 ++ 0x10000E5 0x01 ++ 0x10000B3 0x00 ++ 0x10000E5 0x00 ++ 0x10000F0 0x36 0xA5 0x53 ++ 0x10000E0 0x00 0x35 0x33 0x00 0x00 0x00 0x00 0x35 0x33 0x00 0x00 0x00 ++ 0x100003A 0x55 ++ 0x1000011 ++ 0x2000001 ++ 0x1000029>; ++ }; ++ ++ tinylcd35_ts: tinylcd35_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <5 2>; /* high-to-low edge triggered */ ++ interrupt-parent = <&gpio>; ++ pendown-gpio = <&gpio 5 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++ }; ++ }; ++ ++ fragment@3 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ pcf8563: pcf8563@51 { ++ compatible = "nxp,pcf8563"; ++ reg = <0x51>; ++ status = "disabled"; ++ }; ++ }; ++ }; ++ ++ /* ++ * Values for input event code is found under the ++ * 'Keys and buttons' heading in include/uapi/linux/input.h ++ */ ++ fragment@4 { ++ target-path = "/soc"; ++ __overlay__ { ++ keypad: keypad { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&keypad_pins>; ++ status = "disabled"; ++ autorepeat; ++ ++ button@17 { ++ label = "GPIO KEY_UP"; ++ linux,code = <103>; ++ gpios = <&gpio 17 0>; ++ }; ++ button@22 { ++ label = "GPIO KEY_DOWN"; ++ linux,code = <108>; ++ gpios = <&gpio 22 0>; ++ }; ++ button@27 { ++ label = "GPIO KEY_LEFT"; ++ linux,code = <105>; ++ gpios = <&gpio 27 0>; ++ }; ++ button@23 { ++ label = "GPIO KEY_RIGHT"; ++ linux,code = <106>; ++ gpios = <&gpio 23 0>; ++ }; ++ button@4 { ++ label = "GPIO KEY_ENTER"; ++ linux,code = <28>; ++ gpios = <&gpio 4 0>; ++ }; ++ }; ++ }; ++ }; ++ ++ __overrides__ { ++ speed = <&tinylcd35>,"spi-max-frequency:0"; ++ rotate = <&tinylcd35>,"rotate:0"; ++ fps = <&tinylcd35>,"fps:0"; ++ debug = <&tinylcd35>,"debug:0"; ++ touch = <&tinylcd35_ts>,"status"; ++ touchgpio = <&tinylcd35_ts_pins>,"brcm,pins:0", ++ <&tinylcd35_ts>,"interrupts:0", ++ <&tinylcd35_ts>,"pendown-gpio:4"; ++ xohms = <&tinylcd35_ts>,"ti,x-plate-ohms;0"; ++ rtc = <&i2c1>,"status", ++ <&pcf8563>,"status"; ++ keypad = <&keypad>,"status"; ++ }; ++}; +diff --git a/drivers/video/fbtft/dts/rpi.dts b/drivers/video/fbtft/dts/rpi.dts +new file mode 100644 +index 0000000..6c3ab6e +--- /dev/null ++++ b/drivers/video/fbtft/dts/rpi.dts +@@ -0,0 +1,414 @@ ++ ++/* ++ * FBTFT Device Tree part for the Raspberry Pi ++ * Add this file to the end of the *rpi-b.dts file ++ * ++ * Please keep it sorted alphabetically on display name ++ * ++ * Notes: ++ * - use ads7846 instead of tsc2046 to get module autoloading ++ * - use polarity 1 to drive backlight initially low (off): ++ * led-gpios = <&gpio 18 1>; ++ */ ++ ++&spi0 { ++ /* this is provided here to make it easy to enable SPI when editing this file */ ++// status = "okay"; ++}; ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28A ++ * ++ */ ++&spi0 { ++ hy28a@0{ ++ compatible = "ilitek,ili9320"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ spi-cpol; ++ spi-cpha; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ hy28a_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Texy ++ * 2.8" TFT + Touch Shield Board (320x240) HY28B ++ * NOT TESTED ++ */ ++&spi0 { ++ hy28b@0{ ++ compatible = "ilitek,ili9325"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ startbyte = <0x70>; ++ reset-gpios = <&gpio 25 0>; ++ led-gpios = <&gpio 18 1>; ++ ++ gamma = "04 1F 4 7 7 0 7 7 6 0\n0F 00 1 7 4 0 0 0 6 7"; ++ ++ init = <0x10000e7 0x0010 ++ 0x1000000 0x0001 ++ 0x1000001 0x0100 ++ 0x1000002 0x0700 ++ 0x1000003 0x1030 ++ 0x1000004 0x0000 ++ 0x1000008 0x0207 ++ 0x1000009 0x0000 ++ 0x100000a 0x0000 ++ 0x100000c 0x0001 ++ 0x100000d 0x0000 ++ 0x100000f 0x0000 ++ 0x1000010 0x0000 ++ 0x1000011 0x0007 ++ 0x1000012 0x0000 ++ 0x1000013 0x0000 ++ 0x2000032 ++ 0x1000010 0x1590 ++ 0x1000011 0x0227 ++ 0x2000032 ++ 0x1000012 0x009c ++ 0x2000032 ++ 0x1000013 0x1900 ++ 0x1000029 0x0023 ++ 0x100002b 0x000e ++ 0x2000032 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000032 ++ 0x1000050 0x0000 ++ 0x1000051 0x00ef ++ 0x1000052 0x0000 ++ 0x1000053 0x013f ++ 0x1000060 0xa700 ++ 0x1000061 0x0001 ++ 0x100006a 0x0000 ++ 0x1000080 0x0000 ++ 0x1000081 0x0000 ++ 0x1000082 0x0000 ++ 0x1000083 0x0000 ++ 0x1000084 0x0000 ++ 0x1000085 0x0000 ++ 0x1000090 0x0010 ++ 0x1000092 0x0000 ++ 0x1000093 0x0003 ++ 0x1000095 0x0110 ++ 0x1000097 0x0000 ++ 0x1000098 0x0000 ++ 0x1000007 0x0133 ++ 0x1000020 0x0000 ++ 0x1000021 0x0000 ++ 0x2000064>; ++ debug = <0>; ++ }; ++ ++ hy28b_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * ITEAD ++ * ITDB02-2.8 ++ * ++ */ ++/ { ++ itdb28 { ++ compatible = "ilitek,ili9325"; ++ status = "disabled"; ++ ++ rotate = <0>; ++ bgr; ++ buswidth = <8>; ++ reset-gpios = <&gpio 17 0>; ++ dc-gpios = <&gpio 3 0>; ++ cs-gpios = <&gpio 27 0>; ++ wr-gpios = <&gpio 2 0>; ++ db-gpios = <&gpio 9 0>, ++ <&gpio 11 0>, ++ <&gpio 18 0>, ++ <&gpio 23 0>, ++ <&gpio 24 0>, ++ <&gpio 25 0>, ++ <&gpio 8 0>, ++ <&gpio 7 0>; ++ /* LED pin drives backlight directly. Use transistor (50mA) */ ++ /* led-gpios = <&gpio 4 1>; */ ++ debug = <0>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Watterott ++ * RPi-Display - 2.8" Touch-Display (320x240) ++ * ++ */ ++&spi0 { ++ rpi-display@0{ ++ compatible = "ilitek,ili9341"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 23 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ rpi-display_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 25>; ++ pendown-gpio = <&gpio 25 0>; ++ ti,x-plate-ohms = /bits/ 16 <60>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Ozzmaker ++ * PiScreen - 3.5" TFT touchscreen (480x320) ++ * ++ */ ++&spi0 { ++ piscreen@0{ ++ compatible = "ilitek,ili9486"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <20000000>; ++ rotate = <270>; ++ bgr; ++ fps = <30>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 22 1>; ++ ++ init = <0x10000b0 0x00 ++ 0x1000011 ++ 0x20000FF ++ 0x100003A 0x55 ++ 0x1000036 0x28 ++ 0x10000C2 0x44 ++ 0x10000C5 0x00 0x00 0x00 0x00 ++ 0x10000E0 0x0F 0x1F 0x1C 0x0C 0x0F 0x08 0x48 0x98 0x37 0x0A 0x13 0x04 0x11 0x0D 0x00 ++ 0x10000E1 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x10000E2 0x0F 0x32 0x2E 0x0B 0x0D 0x05 0x47 0x75 0x37 0x06 0x10 0x03 0x24 0x20 0x00 ++ 0x1000011 ++ 0X1000029>; ++ debug = <0>; ++ }; ++ ++ piscreen_ts@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 17>; ++ pendown-gpio = <&gpio 17 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * Adafruit ++ * PiTFT Mini Kit - 320x240 2.8" TFT+Touchscreen for Raspberry Pi ++ * ++ * Couldn't get touch controller to work ++ */ ++ ++&gpio { ++ stmpets_pins: stmpets_pins { ++ brcm,pins = <24>; ++ brcm,function = <0>; /* gpio in */ ++ brcm,pull = <2>; /* pull up */ ++ }; ++}; ++ ++&spi0 { ++ pitft@0{ ++ compatible = "ilitek,ili9340"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <32000000>; ++ rotate = <90>; ++ bgr; ++ buswidth = <8>; ++ dc-gpios = <&gpio 25 0>; ++ ++ init = <0x1000001 ++ 0x2000005 ++ 0x1000028 ++ 0x10000EF 0x03 0x80 0x02 ++ 0x10000CF 0x00 0xC1 0x30 ++ 0x10000ED 0x64 0x03 0x12 0x81 ++ 0x10000E8 0x85 0x00 0x78 ++ 0x10000CB 0x39 0x2C 0x00 0x34 0x02 ++ 0x10000F7 0x20 ++ 0x10000EA 0x00 0x00 ++ 0x10000C0 0x23 ++ 0x10000C1 0x10 ++ 0x10000C5 0x3e 0x28 ++ 0x10000C7 0x86 ++ 0x100003A 0x55 ++ 0x10000B1 0x00 0x18 ++ 0x10000B6 0x08 0x82 0x27 ++ 0x10000F2 0x00 ++ 0x1000026 0x01 ++ 0x10000E0 0x0F 0x31 0x2B 0x0C 0x0E 0x08 0x4E 0xF1 0x37 0x07 0x10 0x03 0x0E 0x09 0x00 ++ 0x10000E1 0x00 0x0E 0x14 0x03 0x11 0x07 0x31 0xC1 0x48 0x08 0x0F 0x0C 0x31 0x36 0x0F ++ 0x1000011 ++ 0x2000064 ++ 0x1000029 ++ 0x2000014>; ++ debug = <0>; ++ }; ++/* ++Touchscreen didn't work. I guess it has something to do with it being an interrupt controller. ++I have never tried this before with DT and ARCH_BCM2708. ++On ARCH_BCM2835 it should be OK, since the GPIO controller acts as an interrupt controller as well. ++(stmpe_device can't be used from 3.16, because irq_base can't be set anymore) ++ ++[ 8.889056] irq: irq_create_mapping(0xc3008800, 0xc2) ++[ 8.895698] irq: -> using domain @c3008800 ++[ 8.900796] irq: -> existing mapping on virq 194 ++[ 8.925048] stmpe-spi spi0.1: stmpe610 detected, chip id: 0x811 ++[ 8.936650] irq: Added domain (null) ++[ 8.941780] irq: irq_create_mapping(0xc1dc1a20, 0x0) ++[ 8.949047] irq: -> using domain @c1dc1a20 ++[ 8.954421] irq: -> virq allocation failed ++[ 8.959926] irq: irq_create_mapping(0xc1dc1a20, 0x1) ++[ 8.967366] irq: -> using domain @c1dc1a20 ++[ 8.972870] irq: -> virq allocation failed ++*/ ++ pitft_ts@1 { ++ compatible = "st,stmpe610"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ status = "disabled"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&stmpets_pins>; ++ ++ spi-max-frequency = <500000>; ++ interrupts = <3 24>; ++ interrupt-parent = <&intc>; ++ interrupt-controller; ++ ++ stmpe_touchscreen { ++ compatible = "st,stmpe-ts"; ++ st,sample-time = <4>; ++ st,mod-12b = <1>; ++ st,ref-sel = <0>; ++ st,adc-freq = <2>; ++ st,ave-ctrl = <3>; ++ st,touch-det-delay = <4>; ++ st,settling = <2>; ++ st,fraction-z = <7>; ++ st,i-drive = <0>; ++ }; ++ }; ++}; ++ ++ ++ ++ ++/* ++ * NeoSec LLC ++ * 3.5 inch TFT Display (320x480) ++ * ++ */ ++&spi0 { ++ tinylcd35@0{ ++ compatible = "neosec,tinylcd"; ++ reg = <0>; ++ status = "disabled"; ++ ++ spi-max-frequency = <48000000>; ++ rotate = <270>; ++ bgr; ++ fps = <50>; ++ buswidth = <8>; ++ reset-gpios = <&gpio 25 0>; ++ dc-gpios = <&gpio 24 0>; ++ led-gpios = <&gpio 18 1>; ++ debug = <0>; ++ }; ++ ++ tinylcd35@1 { ++ compatible = "ti,ads7846"; ++ reg = <1>; ++ status = "disabled"; ++ ++ spi-max-frequency = <2000000>; ++ interrupts = <3 3>; ++ pendown-gpio = <&gpio 3 0>; ++ ti,x-plate-ohms = /bits/ 16 <100>; ++ ti,pressure-max = /bits/ 16 <255>; ++ }; ++}; +diff --git a/drivers/video/fbtft/fb_agm1264k-fl.c b/drivers/video/fbtft/fb_agm1264k-fl.c +new file mode 100644 +index 0000000..7fe4fa0 +--- /dev/null ++++ b/drivers/video/fbtft/fb_agm1264k-fl.c +@@ -0,0 +1,462 @@ ++/* ++ * FB driver for Two KS0108 LCD controllers in AGM1264K-FL display ++ * ++ * Copyright (C) 2014 ololoshka2871 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++/* Uncomment text line to use negative image on display */ ++/*#define NEGATIVE*/ ++ ++#define WHITE 0xff ++#define BLACK 0 ++ ++#define DRVNAME "fb_agm1264k-fl" ++#define WIDTH 64 ++#define HEIGHT 64 ++#define TOTALWIDTH (WIDTH * 2) /* because 2 x ks0108 in one display */ ++#define FPS 20 ++ ++#define EPIN gpio.wr ++#define RS gpio.dc ++#define RW gpio.aux[2] ++#define CS0 gpio.aux[0] ++#define CS1 gpio.aux[1] ++ ++ ++/* diffusing error (“Floyd-Steinberg”) */ ++#define DIFFUSING_MATRIX_WIDTH 2 ++#define DIFFUSING_MATRIX_HEIGHT 2 ++ ++static const signed char ++diffusing_matrix[DIFFUSING_MATRIX_WIDTH][DIFFUSING_MATRIX_HEIGHT] = { ++ {-1, 3}, ++ {3, 2}, ++}; ++ ++static const unsigned char gamma_correction_table[] = { ++0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, ++1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, ++6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, ++13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, ++22, 22, 23, 23, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30, 31, 32, ++33, 33, 34, 35, 35, 36, 37, 38, 39, 39, 40, 41, 42, 43, 43, 44, 45, ++46, 47, 48, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, ++62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 73, 74, 75, 76, 77, 78, 79, 81, ++82, 83, 84, 85, 87, 88, 89, 90, 91, 93, 94, 95, 97, 98, 99, 100, 102, ++103, 105, 106, 107, 109, 110, 111, 113, 114, 116, 117, 119, 120, 121, ++123, 124, 126, 127, 129, 130, 132, 133, 135, 137, 138, 140, 141, 143, ++145, 146, 148, 149, 151, 153, 154, 156, 158, 159, 161, 163, 165, 166, ++168, 170, 172, 173, 175, 177, 179, 181, 182, 184, 186, 188, 190, 192, ++194, 196, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, ++221, 223, 225, 227, 229, 231, 234, 236, 238, 240, 242, 244, 246, 248, ++251, 253, 255 ++}; ++ ++static int init_display(struct fbtft_par *par) ++{ ++ u8 i; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ for (i = 0; i < 2; ++i) { ++ write_reg(par, i, 0x3f); /* display on */ ++ write_reg(par, i, 0x40); /* set x to 0 */ ++ write_reg(par, i, 0xb0); /* set page to 0 */ ++ write_reg(par, i, 0xc0); /* set start line to 0 */ ++ } ++ ++ return 0; ++} ++ ++void reset(struct fbtft_par *par) ++{ ++ if (par->gpio.reset == -1) ++ return; ++ ++ fbtft_dev_dbg(DEBUG_RESET, par, par->info->device, "%s()\n", __func__); ++ ++ gpio_set_value(par->gpio.reset, 0); ++ udelay(20); ++ gpio_set_value(par->gpio.reset, 1); ++ mdelay(120); ++} ++ ++/* Check if all necessary GPIOS defined */ ++static int verify_gpios(struct fbtft_par *par) ++{ ++ int i; ++ ++ fbtft_dev_dbg(DEBUG_VERIFY_GPIOS, par, par->info->device, ++ "%s()\n", __func__); ++ ++ if (par->EPIN < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'wr' (aka E) gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ for (i = 0; i < 8; ++i) { ++ if (par->gpio.db[i] < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'db[%i]' gpio. Aborting.\n", ++ i); ++ return -EINVAL; ++ } ++ } ++ if (par->CS0 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs0' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->CS1 < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'cs1' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ if (par->RW < 0) { ++ dev_err(par->info->device, ++ "Missing info about 'rw' gpio. Aborting.\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static unsigned long ++request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio) ++{ ++ fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device, ++ "%s('%s')\n", __func__, gpio->name); ++ ++ if (strcasecmp(gpio->name, "wr") == 0) { ++ /* left ks0108 E pin */ ++ par->EPIN = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } else if (strcasecmp(gpio->name, "cs0") == 0) { ++ /* left ks0108 controller pin */ ++ par->CS0 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } else if (strcasecmp(gpio->name, "cs1") == 0) { ++ /* right ks0108 controller pin */ ++ par->CS1 = gpio->gpio; ++ return GPIOF_OUT_INIT_HIGH; ++ } ++ ++ /* if write (rw = 0) e(1->0) perform write */ ++ /* if read (rw = 1) e(0->1) set data on D0-7*/ ++ else if (strcasecmp(gpio->name, "rw") == 0) { ++ par->RW = gpio->gpio; ++ return GPIOF_OUT_INIT_LOW; ++ } ++ ++ return FBTFT_GPIO_NO_MATCH; ++} ++ ++/* This function oses to enter commands ++ * first byte - destination controller 0 or 1 ++ * folowing - commands ++ */ ++static void write_reg8_bus8(struct fbtft_par *par, int len, ...) ++{ ++ va_list args; ++ int i, ret; ++ u8 *buf = (u8 *)par->buf; ++ ++ if (unlikely(par->debug & DEBUG_WRITE_REGISTER)) { ++ va_start(args, len); ++ for (i = 0; i < len; i++) ++ buf[i] = (u8)va_arg(args, unsigned int); ++ ++ va_end(args); ++ fbtft_par_dbg_hex(DEBUG_WRITE_REGISTER, par, ++ par->info->device, u8, buf, len, "%s: ", __func__); ++ } ++ ++ va_start(args, len); ++ ++ *buf = (u8)va_arg(args, unsigned int); ++ ++ if (*buf > 1) { ++ va_end(args); ++ dev_err(par->info->device, "%s: Incorrect chip sellect request (%d)\n", ++ __func__, *buf); ++ return; ++ } ++ ++ /* select chip */ ++ if (*buf) { ++ /* cs1 */ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 0); ++ } else { ++ /* cs0 */ ++ gpio_set_value(par->CS0, 0); ++ gpio_set_value(par->CS1, 1); ++ } ++ ++ gpio_set_value(par->RS, 0); /* RS->0 (command mode) */ ++ len--; ++ ++ if (len) { ++ i = len; ++ while (i--) ++ *buf++ = (u8)va_arg(args, unsigned int); ++ ret = par->fbtftops.write(par, par->buf, len * (sizeof(u8))); ++ if (ret < 0) { ++ va_end(args); ++ dev_err(par->info->device, "%s: write() failed and returned %d\n", ++ __func__, ret); ++ return; ++ } ++ } ++ ++ va_end(args); ++} ++ ++static struct ++{ ++ int xs, ys_page, xe, ye_page; ++} addr_win; ++ ++/* save display writing zone */ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ addr_win.xs = xs; ++ addr_win.ys_page = ys / 8; ++ addr_win.xe = xe; ++ addr_win.ye_page = ye / 8; ++ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys_page=%d, xe=%d, ye_page=%d)\n", __func__, ++ addr_win.xs, addr_win.ys_page, addr_win.xe, addr_win.ye_page); ++} ++ ++static void ++construct_line_bitmap(struct fbtft_par *par, u8 *dest, signed short *src, ++ int xs, int xe, int y) ++{ ++ int x, i; ++ ++ for (x = xs; x < xe; ++x) { ++ u8 res = 0; ++ ++ for (i = 0; i < 8; i++) ++ if (src[(y * 8 + i) * par->info->var.xres + x]) ++ res |= 1 << i; ++#ifdef NEGATIVE ++ *dest++ = res; ++#else ++ *dest++ = ~res; ++#endif ++ } ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y; ++ int ret = 0; ++ ++ /* buffer to convert RGB565 -> grayscale16 -> Ditherd image 1bpp */ ++ signed short *convert_buf = kmalloc(par->info->var.xres * ++ par->info->var.yres * sizeof(signed short), GFP_NOIO); ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ /* converting to grayscale16 */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ u16 pixel = vmem16[y * par->info->var.xres + x]; ++ u16 b = pixel & 0x1f; ++ u16 g = (pixel & (0x3f << 5)) >> 5; ++ u16 r = (pixel & (0x1f << (5 + 6))) >> (5 + 6); ++ ++ pixel = (299 * r + 587 * g + 114 * b) / 200; ++ if (pixel > 255) ++ pixel = 255; ++ ++ /* gamma-correction by table */ ++ convert_buf[y * par->info->var.xres + x] = ++ (signed short)gamma_correction_table[pixel]; ++ } ++ ++ /* Image Dithering */ ++ for (x = 0; x < par->info->var.xres; ++x) ++ for (y = 0; y < par->info->var.yres; ++y) { ++ signed short pixel = ++ convert_buf[y * par->info->var.xres + x]; ++ signed short error_b = pixel - BLACK; ++ signed short error_w = pixel - WHITE; ++ signed short error; ++ u16 i, j; ++ ++ /* what color close? */ ++ if (abs(error_b) >= abs(error_w)) { ++ /* white */ ++ error = error_w; ++ pixel = 0xff; ++ } else { ++ /* black */ ++ error = error_b; ++ pixel = 0; ++ } ++ ++ error /= 8; ++ ++ /* diffusion matrix row */ ++ for (i = 0; i < DIFFUSING_MATRIX_WIDTH; ++i) ++ /* diffusion matrix column */ ++ for (j = 0; j < DIFFUSING_MATRIX_HEIGHT; ++j) { ++ signed short *write_pos; ++ signed char coeff; ++ ++ /* skip pixels out of zone */ ++ if (x + i < 0 || ++ x + i >= par->info->var.xres ++ || y + j >= par->info->var.yres) ++ continue; ++ write_pos = &convert_buf[ ++ (y + j) * par->info->var.xres + ++ x + i]; ++ coeff = diffusing_matrix[i][j]; ++ if (coeff == -1) ++ /* pixel itself */ ++ *write_pos = pixel; ++ else { ++ signed short p = *write_pos + ++ error * coeff; ++ ++ if (p > WHITE) ++ p = WHITE; ++ if (p < BLACK) ++ p = BLACK; ++ *write_pos = p; ++ } ++ } ++ } ++ ++ /* 1 string = 2 pages */ ++ for (y = addr_win.ys_page; y <= addr_win.ye_page; ++y) { ++ /* left half of display */ ++ if (addr_win.xs < par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, convert_buf, ++ addr_win.xs, par->info->var.xres / 2, y); ++ ++ len = par->info->var.xres / 2 - addr_win.xs; ++ ++ /* select left side (sc0) ++ * set addr ++ */ ++ write_reg(par, 0x00, (1 << 6) | (u8)addr_win.xs); ++ write_reg(par, 0x00, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ ret = par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ /* right half of display */ ++ if (addr_win.xe >= par->info->var.xres / 2) { ++ construct_line_bitmap(par, buf, ++ convert_buf, par->info->var.xres / 2, ++ addr_win.xe + 1, y); ++ ++ len = addr_win.xe + 1 - par->info->var.xres / 2; ++ ++ /* select right side (sc1) ++ * set addr ++ */ ++ write_reg(par, 0x01, (1 << 6)); ++ write_reg(par, 0x01, (0x17 << 3) | (u8)y); ++ ++ /* write bitmap */ ++ gpio_set_value(par->RS, 1); /* RS->1 (data mode) */ ++ par->fbtftops.write(par, buf, len); ++ if (ret < 0) ++ dev_err(par->info->device, ++ "%s: write failed and returned: %d\n", ++ __func__, ret); ++ } ++ } ++ kfree(convert_buf); ++ ++ gpio_set_value(par->CS0, 1); ++ gpio_set_value(par->CS1, 1); ++ ++ return ret; ++} ++ ++static int write(struct fbtft_par *par, void *buf, size_t len) ++{ ++ fbtft_par_dbg_hex(DEBUG_WRITE, par, par->info->device, u8, buf, len, ++ "%s(len=%d): ", __func__, len); ++ ++ gpio_set_value(par->RW, 0); /* set write mode */ ++ ++ ++ while (len--) { ++ u8 i, data; ++ ++ data = *(u8 *) buf++; ++ ++ /* set data bus */ ++ for (i = 0; i < 8; ++i) ++ gpio_set_value(par->gpio.db[i], data & (1 << i)); ++ /* set E */ ++ gpio_set_value(par->EPIN, 1); ++ udelay(5); ++ /* unset E - write */ ++ gpio_set_value(par->EPIN, 0); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = TOTALWIDTH, ++ .height = HEIGHT, ++ .fps = FPS, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .verify_gpios = verify_gpios, ++ .request_gpios_match = request_gpios_match, ++ .reset = reset, ++ .write = write, ++ .write_register = write_reg8_bus8, ++ .write_vmem = write_vmem, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "displaytronic,fb_agm1264k-fl", &display); ++ ++MODULE_ALIAS("platform:" DRVNAME); ++ ++MODULE_DESCRIPTION("Two KS0108 LCD controllers in AGM1264K-FL display"); ++MODULE_AUTHOR("ololoshka2871"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_bd663474.c b/drivers/video/fbtft/fb_bd663474.c +new file mode 100644 +index 0000000..7e00c60 +--- /dev/null ++++ b/drivers/video/fbtft/fb_bd663474.c +@@ -0,0 +1,193 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "hitachi,bd663474", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:bd663474"); ++MODULE_ALIAS("platform:bd663474"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8340bn.c b/drivers/video/fbtft/fb_hx8340bn.c +new file mode 100644 +index 0000000..3939502 +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8340bn.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the HX8340BN LCD Controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8340bn", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8340bn"); ++MODULE_ALIAS("platform:hx8340bn"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8340BN LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8347d.c b/drivers/video/fbtft/fb_hx8347d.c +new file mode 100644 +index 0000000..8139a8f +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8347d.c +@@ -0,0 +1,181 @@ ++/* ++ * FB driver for the HX8347D LCD Controller ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * ++ * Based on driver code found here: https://github.com/watterott/r61505u-Adapter ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8347d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8347d"); ++MODULE_ALIAS("platform:hx8347d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8347D LCD Controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_hx8353d.c b/drivers/video/fbtft/fb_hx8353d.c +new file mode 100644 +index 0000000..c9512dc +--- /dev/null ++++ b/drivers/video/fbtft/fb_hx8353d.c +@@ -0,0 +1,166 @@ ++/* ++ * FB driver for the HX8353D LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "himax,hx8353d", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:hx8353d"); ++MODULE_ALIAS("platform:hx8353d"); ++ ++MODULE_DESCRIPTION("FB driver for the HX8353D LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9320.c b/drivers/video/fbtft/fb_ili9320.c +new file mode 100644 +index 0000000..b26d893 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9320.c +@@ -0,0 +1,234 @@ ++/* ++ * FB driver for the ILI9320 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9320", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9320"); ++MODULE_ALIAS("platform:ili9320"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9325.c b/drivers/video/fbtft/fb_ili9325.c +new file mode 100644 +index 0000000..5f88145 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9325.c +@@ -0,0 +1,291 @@ ++/* ++ * FB driver for the ILI9325 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Based on ili9325.c by Jeroen Domburg ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9325", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9325"); ++MODULE_ALIAS("platform:ili9325"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9325 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9340.c b/drivers/video/fbtft/fb_ili9340.c +new file mode 100644 +index 0000000..985687d +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9340.c +@@ -0,0 +1,163 @@ ++/* ++ * FB driver for the ILI9340 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9340", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9340"); ++MODULE_ALIAS("platform:ili9340"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9340 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9341.c b/drivers/video/fbtft/fb_ili9341.c +new file mode 100644 +index 0000000..225b2d8 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9341.c +@@ -0,0 +1,179 @@ ++/* ++ * FB driver for the ILI9341 LCD display controller ++ * ++ * This display uses 9-bit SPI: Data/Command bit + 8 data bits ++ * For platforms that doesn't support 9-bit, the driver is capable ++ * of emulating this using 8-bit transfer. ++ * This is done by transfering eight 9-bit words in 9 bytes. ++ * ++ * Copyright (C) 2013 Christian Vogelgsang ++ * Based on adafruit22fb.c by Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "ilitek,ili9341", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9341"); ++MODULE_ALIAS("platform:ili9341"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller"); ++MODULE_AUTHOR("Christian Vogelgsang"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9481.c b/drivers/video/fbtft/fb_ili9481.c +new file mode 100644 +index 0000000..725157a +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9481.c +@@ -0,0 +1,117 @@ ++/* ++ * FB driver for the ILI9481 LCD Controller ++ * ++ * Copyright (c) 2014 Petr Olivka ++ * Copyright (c) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_ili9481" ++#define WIDTH 320 ++#define HEIGHT 480 ++ ++static int default_init_sequence[] = { ++ ++ /* SLP_OUT - Sleep out */ ++ -1, 0x11, ++ -2, 50, ++ /* Power setting */ ++ -1, 0xD0, 0x07, 0x42, 0x18, ++ /* VCOM */ ++ -1, 0xD1, 0x00, 0x07, 0x10, ++ /* Power setting for norm. mode */ ++ -1, 0xD2, 0x01, 0x02, ++ /* Panel driving setting */ ++ -1, 0xC0, 0x10, 0x3B, 0x00, 0x02, 0x11, ++ /* Frame rate & inv. */ ++ -1, 0xC5, 0x03, ++ /* Pixel format */ ++ -1, 0x3A, 0x55, ++ /* Gamma */ ++ -1, 0xC8, 0x00, 0x32, 0x36, 0x45, 0x06, 0x16, ++ 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00, ++ /* DISP_ON */ ++ -1, 0x29, ++ -3 ++}; ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, ++ "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* column address */ ++ write_reg(par, 0x2a, xs >> 8, xs & 0xff, xe >> 8, xe & 0xff); ++ ++ /* row adress */ ++ write_reg(par, 0x2b, ys >> 8, ys & 0xff, ye >> 8, ye & 0xff); ++ ++ /* memory write */ ++ write_reg(par, 0x2c); ++} ++ ++#define HFLIP 0x01 ++#define VFLIP 0x02 ++#define ROWxCOL 0x20 ++static int set_var(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ switch (par->info->var.rotate) { ++ case 270: ++ write_reg(par, 0x36, ROWxCOL | HFLIP | VFLIP | (par->bgr << 3)); ++ break; ++ case 180: ++ write_reg(par, 0x36, VFLIP | (par->bgr << 3)); ++ break; ++ case 90: ++ write_reg(par, 0x36, ROWxCOL | (par->bgr << 3)); ++ break; ++ default: ++ write_reg(par, 0x36, HFLIP | (par->bgr << 3)); ++ break; ++ } ++ ++ return 0; ++} ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9481", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9481"); ++MODULE_ALIAS("platform:ili9481"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9481 LCD Controller"); ++MODULE_AUTHOR("Petr Olivka"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ili9486.c b/drivers/video/fbtft/fb_ili9486.c +new file mode 100644 +index 0000000..95b8999 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ili9486.c +@@ -0,0 +1,121 @@ ++/* ++ * FB driver for the ILI9486 LCD Controller ++ * ++ * Copyright (C) 2014 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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 = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .init_sequence = default_init_sequence, ++ .fbtftops = { ++ .set_addr_win = set_addr_win, ++ .set_var = set_var, ++ }, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9486", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ili9486"); ++MODULE_ALIAS("platform:ili9486"); ++ ++MODULE_DESCRIPTION("FB driver for the ILI9486 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_pcd8544.c b/drivers/video/fbtft/fb_pcd8544.c +new file mode 100644 +index 0000000..678ab8e +--- /dev/null ++++ b/drivers/video/fbtft/fb_pcd8544.c +@@ -0,0 +1,177 @@ ++/* ++ * FB driver for the PCD8544 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "philips,pdc8544", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:pdc8544"); ++ ++MODULE_DESCRIPTION("FB driver for the PCD8544 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ra8875.c b/drivers/video/fbtft/fb_ra8875.c +new file mode 100644 +index 0000000..c323c06 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ra8875.c +@@ -0,0 +1,331 @@ ++/****************************************************************************** ++ ++ 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, "raio,ra8875", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ra8875"); ++MODULE_ALIAS("platform:ra8875"); ++ ++MODULE_DESCRIPTION("FB driver for the RA8875 LCD Controller"); ++MODULE_AUTHOR("Pf@nne"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d02a1.c b/drivers/video/fbtft/fb_s6d02a1.c +new file mode 100644 +index 0000000..e412a42 +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d02a1.c +@@ -0,0 +1,168 @@ ++/* ++ * FB driver for the S6D02A1 LCD Controller ++ * ++ * Based on fb_st7735r.c by Noralf Tronnes ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d02a1", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d02a1"); ++MODULE_ALIAS("platform:s6d02a1"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D02A1 LCD Controller"); ++MODULE_AUTHOR("WOLFGANG BUENING"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_s6d1121.c b/drivers/video/fbtft/fb_s6d1121.c +new file mode 100644 +index 0000000..1ef8c1a +--- /dev/null ++++ b/drivers/video/fbtft/fb_s6d1121.c +@@ -0,0 +1,208 @@ ++/* ++ * FB driver for the S6D1121 LCD Controller ++ * ++ * Copyright (C) 2013 Roman Rolinsky ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "samsung,s6d1121", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:s6d1121"); ++MODULE_ALIAS("platform:s6d1121"); ++ ++MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller"); ++MODULE_AUTHOR("Roman Rolinsky"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1289.c b/drivers/video/fbtft/fb_ssd1289.c +new file mode 100644 +index 0000000..ef46fbc +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1289.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the SSD1289 LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * Init sequence taken from ITDB02_Graph16.cpp - (C)2010-2011 Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1289", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1289"); ++MODULE_ALIAS("platform:ssd1289"); ++ ++MODULE_DESCRIPTION("FB driver for the SSD1289 LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1306.c b/drivers/video/fbtft/fb_ssd1306.c +new file mode 100644 +index 0000000..5ea195b +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1306.c +@@ -0,0 +1,229 @@ ++/* ++ * FB driver for the SSD1306 OLED Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "solomon,ssd1306", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1306"); ++MODULE_ALIAS("platform:ssd1306"); ++ ++MODULE_DESCRIPTION("SSD1306 OLED Driver"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1331.c b/drivers/video/fbtft/fb_ssd1331.c +new file mode 100644 +index 0000000..da7464f +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1331.c +@@ -0,0 +1,205 @@ ++#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, "solomon,ssd1331", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1331"); ++MODULE_ALIAS("platform:ssd1331"); ++ ++MODULE_DESCRIPTION("SSD1331 OLED Driver"); ++MODULE_AUTHOR("Alec Smecher (adapted from SSD1351 by James Davies)"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_ssd1351.c b/drivers/video/fbtft/fb_ssd1351.c +new file mode 100644 +index 0000000..062d986 +--- /dev/null ++++ b/drivers/video/fbtft/fb_ssd1351.c +@@ -0,0 +1,258 @@ ++#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, "solomon,ssd1351", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:ssd1351"); ++MODULE_ALIAS("platform:ssd1351"); ++ ++MODULE_DESCRIPTION("SSD1351 OLED Driver"); ++MODULE_AUTHOR("James Davies"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_st7735r.c b/drivers/video/fbtft/fb_st7735r.c +new file mode 100644 +index 0000000..b63aa38 +--- /dev/null ++++ b/drivers/video/fbtft/fb_st7735r.c +@@ -0,0 +1,195 @@ ++/* ++ * FB driver for the ST7735R LCD Controller ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "sitronix,st7735r", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:st7735r"); ++MODULE_ALIAS("platform:st7735r"); ++ ++MODULE_DESCRIPTION("FB driver for the ST7735R LCD Controller"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tinylcd.c b/drivers/video/fbtft/fb_tinylcd.c +new file mode 100644 +index 0000000..ca98bfb +--- /dev/null ++++ b/drivers/video/fbtft/fb_tinylcd.c +@@ -0,0 +1,124 @@ ++/* ++ * Custom FB driver for tinylcd.com display ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "neosec,tinylcd", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tinylcd"); ++ ++MODULE_DESCRIPTION("Custom FB driver for tinylcd.com display"); ++MODULE_AUTHOR("Noralf Tronnes"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_tls8204.c b/drivers/video/fbtft/fb_tls8204.c +new file mode 100644 +index 0000000..8738c7a +--- /dev/null ++++ b/drivers/video/fbtft/fb_tls8204.c +@@ -0,0 +1,176 @@ ++/* ++ * FB driver for the TLS8204 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2013 Noralf Tronnes ++ * Copyright (C) 2014 Michael Hope (adapted for the TLS8204) ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "teralane,tls8204", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:tls8204"); ++ ++MODULE_DESCRIPTION("FB driver for the TLS8204 LCD Controller"); ++MODULE_AUTHOR("Michael Hope"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_uc1701.c b/drivers/video/fbtft/fb_uc1701.c +new file mode 100644 +index 0000000..d70ac52 +--- /dev/null ++++ b/drivers/video/fbtft/fb_uc1701.c +@@ -0,0 +1,210 @@ ++/* ++ * FB driver for the UC1701 LCD Controller ++ * ++ * The display is monochrome and the video memory is RGB565. ++ * Any pixel value except 0 turns the pixel on. ++ * ++ * Copyright (C) 2014 Juergen Holzmann ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fbtft.h" ++ ++#define DRVNAME "fb_uc1701" ++#define WIDTH 102 ++#define HEIGHT 64 ++#define PAGES (HEIGHT/8) ++ ++/* 1: Display on/off */ ++#define LCD_DISPLAY_ENABLE 0xAE ++/* 2: display start line set */ ++#define LCD_START_LINE 0x40 ++/* 3: Page address set (lower 4 bits select one of the pages) */ ++#define LCD_PAGE_ADDRESS 0xB0 ++/* 4: column address */ ++#define LCD_COL_ADDRESS 0x10 ++/* 8: select orientation */ ++#define LCD_BOTTOMVIEW 0xA0 ++/* 9: inverted display */ ++#define LCD_DISPLAY_INVERT 0xA6 ++/* 10: show memory content or switch all pixels on */ ++#define LCD_ALL_PIXEL 0xA4 ++/* 11: lcd bias set */ ++#define LCD_BIAS 0xA2 ++/* 14: Reset Controller */ ++#define LCD_RESET_CMD 0xE2 ++/* 15: output mode select (turns display upside-down) */ ++#define LCD_SCAN_DIR 0xC0 ++/* 16: power control set */ ++#define LCD_POWER_CONTROL 0x28 ++/* 17: voltage regulator resistor ratio set */ ++#define LCD_VOLTAGE 0x20 ++/* 18: Volume mode set */ ++#define LCD_VOLUME_MODE 0x81 ++/* 22: NOP command */ ++#define LCD_NO_OP 0xE3 ++/* 25: advanced program control */ ++#define LCD_ADV_PROG_CTRL 0xFA ++/* 25: advanced program control2 */ ++#define LCD_ADV_PROG_CTRL2 0x10 ++#define LCD_TEMPCOMP_HIGH 0x80 ++/* column offset for normal orientation */ ++#define SHIFT_ADDR_NORMAL 0 ++/* column offset for bottom view orientation */ ++#define SHIFT_ADDR_TOPVIEW 30 ++ ++ ++static int init_display(struct fbtft_par *par) ++{ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ par->fbtftops.reset(par); ++ ++ /* softreset of LCD */ ++ write_reg(par, LCD_RESET_CMD); ++ mdelay(10); ++ ++ /* set startpoint */ ++ /* LCD_START_LINE | (pos & 0x3F) */ ++ write_reg(par, LCD_START_LINE); ++ ++ /* select orientation BOTTOMVIEW */ ++ write_reg(par, LCD_BOTTOMVIEW | 1); ++ /* output mode select (turns display upside-down) */ ++ write_reg(par, LCD_SCAN_DIR | 0x00); ++ ++ /* Normal Pixel mode */ ++ write_reg(par, LCD_ALL_PIXEL | 0); ++ ++ /* positive display */ ++ write_reg(par, LCD_DISPLAY_INVERT | 0); ++ ++ /* bias 1/9 */ ++ write_reg(par, LCD_BIAS | 0); ++ ++ /* power control mode: all features on */ ++ /* LCD_POWER_CONTROL | (val&0x07) */ ++ write_reg(par, LCD_POWER_CONTROL | 0x07); ++ ++ /* set voltage regulator R/R */ ++ /* LCD_VOLTAGE | (val&0x07) */ ++ write_reg(par, LCD_VOLTAGE | 0x07); ++ ++ /* volume mode set */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_VOLUME_MODE); ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, 0x09); ++ /* ???? */ ++ /* LCD_VOLUME_MODE,val&0x3f,LCD_NO_OP */ ++ write_reg(par, LCD_NO_OP); ++ ++ /* advanced program control */ ++ write_reg(par, LCD_ADV_PROG_CTRL); ++ write_reg(par, LCD_ADV_PROG_CTRL2|LCD_TEMPCOMP_HIGH); ++ ++ /* enable display */ ++ write_reg(par, LCD_DISPLAY_ENABLE | 1); ++ ++ return 0; ++} ++ ++static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) ++{ ++ fbtft_par_dbg(DEBUG_SET_ADDR_WIN, par, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye); ++ ++ /* goto address */ ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++} ++ ++static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) ++{ ++ u16 *vmem16 = (u16 *)par->info->screen_base; ++ u8 *buf = par->txbuf.buf; ++ int x, y, i; ++ int ret = 0; ++ ++ fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); ++ ++ for (y = 0; y < PAGES; y++) { ++ buf = par->txbuf.buf; ++ for (x = 0; x < WIDTH; x++) { ++ *buf = 0x00; ++ for (i = 0; i < 8; i++) ++ *buf |= (vmem16[((y*8*WIDTH)+(i*WIDTH))+x] ? 1 : 0) << i; ++ buf++; ++ } ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_PAGE_ADDRESS|(u8)y); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, 0x00); ++ /* LCD_PAGE_ADDRESS | ((page) & 0x1F), ++ (((col)+SHIFT_ADDR_NORMAL) & 0x0F), ++ LCD_COL_ADDRESS | ((((col)+SHIFT_ADDR_NORMAL)>>4) & 0x0F) */ ++ write_reg(par, LCD_COL_ADDRESS); ++ gpio_set_value(par->gpio.dc, 1); ++ ret = par->fbtftops.write(par, par->txbuf.buf, WIDTH); ++ gpio_set_value(par->gpio.dc, 0); ++ } ++ ++ if (ret < 0) ++ dev_err(par->info->device, "%s: write failed and returned: %d\n", __func__, ret); ++ ++ return ret; ++} ++ ++ ++static struct fbtft_display display = { ++ .regwidth = 8, ++ .width = WIDTH, ++ .height = HEIGHT, ++ .fbtftops = { ++ .init_display = init_display, ++ .set_addr_win = set_addr_win, ++ .write_vmem = write_vmem, ++ }, ++ .backlight = 1, ++}; ++FBTFT_REGISTER_DRIVER(DRVNAME, "UltraChip,uc1701", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("spi:uc1701"); ++ ++MODULE_DESCRIPTION("FB driver for the UC1701 LCD Controller"); ++MODULE_AUTHOR("Juergen Holzmann"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_upd161704.c b/drivers/video/fbtft/fb_upd161704.c +new file mode 100644 +index 0000000..fff57b3 +--- /dev/null ++++ b/drivers/video/fbtft/fb_upd161704.c +@@ -0,0 +1,206 @@ ++/* ++ * FB driver for the uPD161704 LCD Controller ++ * ++ * Copyright (C) 2014 Seong-Woo Kim ++ * ++ * Based on fb_ili9325.c by Noralf Tronnes ++ * Based on ili9325.c by Jeroen Domburg ++ * Init code from UTFT library by Henning Karlsen ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#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, "nec,upd161704", &display); ++ ++MODULE_ALIAS("spi:" DRVNAME); ++MODULE_ALIAS("platform:" DRVNAME); ++MODULE_ALIAS("spi:upd161704"); ++MODULE_ALIAS("platform:upd161704"); ++ ++MODULE_DESCRIPTION("FB driver for the uPD161704 LCD Controller"); ++MODULE_AUTHOR("Seong-Woo Kim"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/video/fbtft/fb_watterott.c b/drivers/video/fbtft/fb_watterott.c +new file mode 100644 +index 0000000..975b579 +--- /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, "watterott,openlcd", &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..873e2c7 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft-core.c +@@ -0,0 +1,1516 @@ ++/* ++ * 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 ++#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 = true; ++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 ret; ++ ++ 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 = devm_gpio_request_one(par->info->device, ++ 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; ++} ++ ++#ifdef CONFIG_OF ++static int fbtft_request_one_gpio(struct fbtft_par *par, ++ const char *name, int index, int *gpiop) ++{ ++ struct device *dev = par->info->device; ++ struct device_node *node = dev->of_node; ++ int gpio, flags, ret = 0; ++ enum of_gpio_flags of_flags; ++ ++ if (of_find_property(node, name, NULL)) { ++ gpio = of_get_named_gpio_flags(node, name, index, &of_flags); ++ if (gpio == -ENOENT) ++ return 0; ++ if (gpio == -EPROBE_DEFER) ++ return gpio; ++ if (gpio < 0) { ++ dev_err(dev, ++ "failed to get '%s' from DT\n", name); ++ return gpio; ++ } ++ ++ /* active low translates to initially low */ ++ flags = (of_flags & OF_GPIO_ACTIVE_LOW) ? GPIOF_OUT_INIT_LOW : ++ GPIOF_OUT_INIT_HIGH; ++ ret = devm_gpio_request_one(dev, gpio, flags, ++ dev->driver->name); ++ if (ret) { ++ dev_err(dev, ++ "gpio_request_one('%s'=%d) failed with %d\n", ++ name, gpio, ret); ++ return ret; ++ } ++ if (gpiop) ++ *gpiop = gpio; ++ fbtft_par_dbg(DEBUG_REQUEST_GPIOS, par, "%s: '%s' = GPIO%d\n", ++ __func__, name, gpio); ++ } ++ ++ return ret; ++} ++ ++static int fbtft_request_gpios_dt(struct fbtft_par *par) ++{ ++ int i; ++ int ret; ++ ++ if (!par->info->device->of_node) ++ return -EINVAL; ++ ++ ret = fbtft_request_one_gpio(par, "reset-gpios", 0, &par->gpio.reset); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "dc-gpios", 0, &par->gpio.dc); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "rd-gpios", 0, &par->gpio.rd); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "wr-gpios", 0, &par->gpio.wr); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "cs-gpios", 0, &par->gpio.cs); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "latch-gpios", 0, &par->gpio.latch); ++ if (ret) ++ return ret; ++ for (i = 0; i < 16; i++) { ++ ret = fbtft_request_one_gpio(par, "db-gpios", i, ++ &par->gpio.db[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "led-gpios", i, ++ &par->gpio.led[i]); ++ if (ret) ++ return ret; ++ ret = fbtft_request_one_gpio(par, "aux-gpios", i, ++ &par->gpio.aux[i]); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#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->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; ++ int vmem_size, i; ++ 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; ++ ++ if (!pdata) { ++ dev_err(dev, "platform data is missing\n"); ++ return NULL; ++ } ++ ++ /* override driver values? */ ++ if (pdata->fps) ++ fps = pdata->fps; ++ if (pdata->txbuflen) ++ txbuflen = pdata->txbuflen; ++ 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; ++ 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; ++ if (pdata->display.regwidth) ++ display->regwidth = pdata->display.regwidth; ++ ++ display->debug |= debug; ++ fbtft_expand_debug_value(&display->debug); ++ ++ switch (pdata->rotate) { ++ case 90: ++ case 270: ++ width = display->height; ++ height = display->width; ++ break; ++ default: ++ width = display->width; ++ height = display->height; ++ } ++ ++ vmem_size = display->width * display->height * bpp / 8; ++ 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 = pdata->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 = pdata->bgr; ++ par->startbyte = pdata->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) { ++ if (fbtft_gamma_parse_str(par, ++ par->gamma.curves, gamma, strlen(gamma))) ++ goto alloc_fail; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ } ++ ++ /* 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; ++ 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); ++ ++ 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); ++ ret = unregister_framebuffer(fb_info); ++ return ret; ++} ++EXPORT_SYMBOL(fbtft_unregister_framebuffer); ++ ++#ifdef CONFIG_OF ++/** ++ * fbtft_init_display_dt() - Device Tree init_display() function ++ * @par: Driver data ++ * ++ * Return: 0 if successful, negative if error ++ */ ++static int fbtft_init_display_dt(struct fbtft_par *par) ++{ ++ struct device_node *node = par->info->device->of_node; ++ struct property *prop; ++ const __be32 *p; ++ u32 val; ++ int buf[64], i, j; ++ char msg[128]; ++ char str[16]; ++ ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "%s()\n", __func__); ++ ++ if (!node) ++ return -EINVAL; ++ ++ prop = of_find_property(node, "init", NULL); ++ p = of_prop_next_u32(prop, NULL, &val); ++ if (!p) ++ return -EINVAL; ++ while (p) { ++ if (val & FBTFT_OF_INIT_CMD) { ++ val &= 0xFFFF; ++ i = 0; ++ while (p && !(val & 0xFFFF0000)) { ++ if (i > 63) { ++ dev_err(par->info->device, ++ "%s: Maximum register values exceeded\n", ++ __func__); ++ return -EINVAL; ++ } ++ buf[i++] = val; ++ p = of_prop_next_u32(prop, p, &val); ++ } ++ /* make debug message */ ++ msg[0] = '\0'; ++ for (j = 0; j < i; j++) { ++ snprintf(str, 128, " %02X", buf[j]); ++ strcat(msg, str); ++ } ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: write_register:%s\n", msg); ++ ++ par->fbtftops.write_register(par, i, ++ 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]); ++ } else if (val & FBTFT_OF_INIT_DELAY) { ++ fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, ++ "init: msleep(%u)\n", val & 0xFFFF); ++ msleep(val & 0xFFFF); ++ p = of_prop_next_u32(prop, p, &val); ++ } else { ++ dev_err(par->info->device, "illegal init value 0x%X\n", ++ val); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++/** ++ * 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->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; ++} ++ ++#ifdef CONFIG_OF ++/* returns 0 if the property is not present */ ++static u32 fbtft_of_value(struct device_node *node, const char *propname) ++{ ++ int ret; ++ u32 val = 0; ++ ++ ret = of_property_read_u32(node, propname, &val); ++ if (ret == 0) ++ pr_info("%s: %s = %u\n", __func__, propname, val); ++ ++ return val; ++} ++ ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ struct device_node *node = dev->of_node; ++ struct fbtft_platform_data *pdata; ++ ++ if (!node) { ++ dev_err(dev, "Missing platform data or DT\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) ++ return ERR_PTR(-ENOMEM); ++ ++ pdata->display.width = fbtft_of_value(node, "width"); ++ pdata->display.height = fbtft_of_value(node, "height"); ++ pdata->display.regwidth = fbtft_of_value(node, "regwidth"); ++ pdata->display.buswidth = fbtft_of_value(node, "buswidth"); ++ pdata->display.backlight = fbtft_of_value(node, "backlight"); ++ pdata->display.bpp = fbtft_of_value(node, "bpp"); ++ pdata->display.debug = fbtft_of_value(node, "debug"); ++ pdata->rotate = fbtft_of_value(node, "rotate"); ++ pdata->bgr = of_property_read_bool(node, "bgr"); ++ pdata->fps = fbtft_of_value(node, "fps"); ++ pdata->txbuflen = fbtft_of_value(node, "txbuflen"); ++ pdata->startbyte = fbtft_of_value(node, "startbyte"); ++ of_property_read_string(node, "gamma", (const char **)&pdata->gamma); ++ ++ if (of_find_property(node, "led-gpios", NULL)) ++ pdata->display.backlight = 1; ++ if (of_find_property(node, "init", NULL)) ++ pdata->display.fbtftops.init_display = fbtft_init_display_dt; ++ pdata->display.fbtftops.request_gpios = fbtft_request_gpios_dt; ++ ++ return pdata; ++} ++#else ++static struct fbtft_platform_data *fbtft_probe_dt(struct device *dev) ++{ ++ dev_err(dev, "Missing platform data\n"); ++ return ERR_PTR(-EINVAL); ++} ++#endif ++ ++/** ++ * 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) { ++ pdata = fbtft_probe_dt(dev); ++ if (IS_ERR(pdata)) ++ return PTR_ERR(pdata); ++ dev->platform_data = pdata; ++ } ++ ++ info = fbtft_framebuffer_alloc(display, dev); ++ if (!info) ++ return -ENOMEM; ++ ++ par = info->par; ++ par->spi = sdev; ++ par->pdev = pdev; ++ ++ if (display->buswidth == 0) { ++ dev_err(dev, "buswidth is not set\n"); ++ return -EINVAL; ++ } ++ ++ /* 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 */ ++ 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..45f8de3 +--- /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, 0660, show_gamma_curve, store_gamma_curve), ++}; ++ ++ ++void fbtft_expand_debug_value(unsigned long *debug) ++{ ++ switch (*debug & 0b111) { ++ case 1: ++ *debug |= DEBUG_LEVEL_1; ++ break; ++ case 2: ++ *debug |= DEBUG_LEVEL_2; ++ break; ++ case 3: ++ *debug |= DEBUG_LEVEL_3; ++ break; ++ case 4: ++ *debug |= DEBUG_LEVEL_4; ++ break; ++ case 5: ++ *debug |= DEBUG_LEVEL_5; ++ break; ++ case 6: ++ *debug |= DEBUG_LEVEL_6; ++ break; ++ case 7: ++ *debug = 0xFFFFFFFF; ++ break; ++ } ++} ++ ++static ssize_t store_debug(struct device *device, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ int ret; ++ ++ ret = kstrtoul(buf, 10, &par->debug); ++ if (ret) ++ return ret; ++ fbtft_expand_debug_value(&par->debug); ++ ++ return count; ++} ++ ++static ssize_t show_debug(struct device *device, ++ struct device_attribute *attr, char *buf) ++{ ++ struct fb_info *fb_info = dev_get_drvdata(device); ++ struct fbtft_par *par = fb_info->par; ++ ++ return snprintf(buf, PAGE_SIZE, "%lu\n", par->debug); ++} ++ ++static struct device_attribute debug_device_attr = \ ++ __ATTR(debug, 0660, show_debug, store_debug); ++ ++ ++void fbtft_sysfs_init(struct fbtft_par *par) ++{ ++ device_create_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_create_file(par->info->dev, &gamma_device_attrs[0]); ++} ++ ++void fbtft_sysfs_exit(struct fbtft_par *par) ++{ ++ device_remove_file(par->info->dev, &debug_device_attr); ++ if (par->gamma.curves && par->fbtftops.set_gamma) ++ device_remove_file(par->info->dev, &gamma_device_attrs[0]); ++} +diff --git a/drivers/video/fbtft/fbtft.h b/drivers/video/fbtft/fbtft.h +new file mode 100644 +index 0000000..0dbf3f9 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft.h +@@ -0,0 +1,447 @@ ++/* ++ * Copyright (C) 2013 Noralf Tronnes ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#ifndef __LINUX_FBTFT_H ++#define __LINUX_FBTFT_H ++ ++#include ++#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 ++ ++#define FBTFT_OF_INIT_CMD BIT(24) ++#define FBTFT_OF_INIT_DELAY BIT(25) ++ ++/** ++ * struct fbtft_gpio - Structure that holds one pinname to gpio mapping ++ * @name: pinname (reset, dc, etc.) ++ * @gpio: GPIO number ++ * ++ */ ++struct fbtft_gpio { ++ char name[FBTFT_GPIO_NAME_SIZE]; ++ unsigned gpio; ++}; ++ ++struct fbtft_par; ++ ++/** ++ * struct fbtft_ops - FBTFT operations structure ++ * @write: Writes to interface bus ++ * @read: Reads from interface bus ++ * @write_vmem: Writes video memory to display ++ * @write_reg: Writes to controller register ++ * @set_addr_win: Set the GRAM update window ++ * @reset: Reset the LCD controller ++ * @mkdirty: Marks display lines for update ++ * @update_display: Updates the display ++ * @init_display: Initializes the display ++ * @blank: Blank the display (optional) ++ * @request_gpios_match: Do pinname to gpio matching ++ * @request_gpios: Request gpios from the kernel ++ * @free_gpios: Free previously requested gpios ++ * @verify_gpios: Verify that necessary gpios is present (optional) ++ * @register_backlight: Used to register backlight device (optional) ++ * @unregister_backlight: Unregister backlight device (optional) ++ * @set_var: Configure LCD with values from variables like @rotate and @bgr ++ * (optional) ++ * @set_gamma: Set Gamma curve (optional) ++ * ++ * Most of these operations have default functions assigned to them in ++ * fbtft_framebuffer_alloc() ++ */ ++struct fbtft_ops { ++ int (*write)(struct fbtft_par *par, void *buf, size_t len); ++ int (*read)(struct fbtft_par *par, void *buf, size_t len); ++ int (*write_vmem)(struct fbtft_par *par, size_t offset, size_t len); ++ void (*write_register)(struct fbtft_par *par, int len, ...); ++ ++ void (*set_addr_win)(struct fbtft_par *par, ++ int xs, int ys, int xe, int ye); ++ void (*reset)(struct fbtft_par *par); ++ void (*mkdirty)(struct fb_info *info, int from, int to); ++ void (*update_display)(struct fbtft_par *par, ++ unsigned start_line, unsigned end_line); ++ int (*init_display)(struct fbtft_par *par); ++ int (*blank)(struct fbtft_par *par, bool on); ++ ++ unsigned long (*request_gpios_match)(struct fbtft_par *par, ++ const struct fbtft_gpio *gpio); ++ int (*request_gpios)(struct fbtft_par *par); ++ int (*verify_gpios)(struct fbtft_par *par); ++ ++ void (*register_backlight)(struct fbtft_par *par); ++ void (*unregister_backlight)(struct fbtft_par *par); ++ ++ int (*set_var)(struct fbtft_par *par); ++ int (*set_gamma)(struct fbtft_par *par, unsigned long *curves); ++}; ++ ++/** ++ * struct fbtft_display - Describes the display properties ++ * @width: Width of display in pixels ++ * @height: Height of display in pixels ++ * @regwidth: LCD Controller Register width in bits ++ * @buswidth: Display interface bus width in bits ++ * @backlight: Backlight type. ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @bpp: Bits per pixel ++ * @fps: Frames per second ++ * @txbuflen: Size of transmit buffer ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma: String representation of Gamma curve(s) ++ * @gamma_num: Number of Gamma curves ++ * @gamma_len: Number of values per Gamma curve ++ * @debug: Initial debug value ++ * ++ * This structure is not stored by FBTFT except for init_sequence. ++ */ ++struct fbtft_display { ++ unsigned width; ++ unsigned height; ++ unsigned regwidth; ++ unsigned buswidth; ++ unsigned backlight; ++ struct fbtft_ops fbtftops; ++ unsigned bpp; ++ unsigned fps; ++ int txbuflen; ++ int *init_sequence; ++ char *gamma; ++ int gamma_num; ++ int gamma_len; ++ unsigned long debug; ++}; ++ ++/** ++ * struct fbtft_platform_data - Passes display specific data to the driver ++ * @display: Display properties ++ * @gpios: Pointer to an array of piname to gpio mappings ++ * @rotate: Display rotation angle ++ * @bgr: LCD Controller BGR bit ++ * @fps: Frames per second (this will go away, use @fps in @fbtft_display) ++ * @txbuflen: Size of transmit buffer ++ * @startbyte: When set, enables use of Startbyte in transfers ++ * @gamma: String representation of Gamma curve(s) ++ * @extra: A way to pass extra info ++ */ ++struct fbtft_platform_data { ++ struct fbtft_display display; ++ const struct fbtft_gpio *gpios; ++ unsigned rotate; ++ bool bgr; ++ unsigned fps; ++ int txbuflen; ++ u8 startbyte; ++ char *gamma; ++ void *extra; ++}; ++ ++/** ++ * struct fbtft_par - Main FBTFT data structure ++ * ++ * This structure holds all relevant data to operate the display ++ * ++ * See sourcefile for documentation since nested structs is not ++ * supported by kernel-doc. ++ * ++ */ ++/* @spi: Set if it is a SPI device ++ * @pdev: Set if it is a platform device ++ * @info: Pointer to framebuffer fb_info structure ++ * @pdata: Pointer to platform data ++ * @ssbuf: Not used ++ * @pseudo_palette: Used by fb_set_colreg() ++ * @txbuf.buf: Transmit buffer ++ * @txbuf.len: Transmit buffer length ++ * @buf: Small buffer used when writing init data over SPI ++ * @startbyte: Used by some controllers when in SPI mode. ++ * Format: 6 bit Device id + RS bit + RW bit ++ * @fbtftops: FBTFT operations provided by driver or device (platform_data) ++ * @dirty_lock: Protects dirty_lines_start and dirty_lines_end ++ * @dirty_lines_start: Where to begin updating display ++ * @dirty_lines_end: Where to end updating display ++ * @gpio.reset: GPIO used to reset display ++ * @gpio.dc: Data/Command signal, also known as RS ++ * @gpio.rd: Read latching signal ++ * @gpio.wr: Write latching signal ++ * @gpio.latch: Bus latch signal, eg. 16->8 bit bus latch ++ * @gpio.cs: LCD Chip Select with parallel interface bus ++ * @gpio.db[16]: Parallel databus ++ * @gpio.led[16]: Led control signals ++ * @gpio.aux[16]: Auxillary signals, not used by core ++ * @init_sequence: Pointer to LCD initialization array ++ * @gamma.lock: Mutex for Gamma curve locking ++ * @gamma.curves: Pointer to Gamma curve array ++ * @gamma.num_values: Number of values per Gamma curve ++ * @gamma.num_curves: Number of Gamma curves ++ * @debug: Pointer to debug value ++ * @current_debug: ++ * @first_update_done: Used to only time the first display update ++ * @update_time: Used to calculate 'fps' in debug output ++ * @bgr: BGR mode/\n ++ * @extra: Extra info needed by driver ++ */ ++struct fbtft_par { ++ struct spi_device *spi; ++ struct platform_device *pdev; ++ struct fb_info *info; ++ struct fbtft_platform_data *pdata; ++ u16 *ssbuf; ++ u32 pseudo_palette[16]; ++ struct { ++ void *buf; ++ dma_addr_t dma; ++ size_t len; ++ } txbuf; ++ u8 *buf; ++ u8 startbyte; ++ struct fbtft_ops fbtftops; ++ spinlock_t dirty_lock; ++ unsigned dirty_lines_start; ++ unsigned dirty_lines_end; ++ struct { ++ int reset; ++ int dc; ++ int rd; ++ int wr; ++ int latch; ++ int cs; ++ int db[16]; ++ int led[16]; ++ int aux[16]; ++ } gpio; ++ int *init_sequence; ++ struct { ++ struct mutex lock; ++ unsigned long *curves; ++ int num_values; ++ int num_curves; ++ } gamma; ++ unsigned long debug; ++ bool first_update_done; ++ struct timespec update_time; ++ bool bgr; ++ void *extra; ++}; ++ ++#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int)) ++ ++#define write_reg(par, ...) \ ++do { \ ++ par->fbtftops.write_register(par, NUMARGS(__VA_ARGS__), __VA_ARGS__); \ ++} while (0) ++ ++/* fbtft-core.c */ ++extern void fbtft_dbg_hex(const struct device *dev, ++ int groupsize, void *buf, size_t len, const char *fmt, ...); ++extern struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, ++ struct device *dev); ++extern void fbtft_framebuffer_release(struct fb_info *info); ++extern int fbtft_register_framebuffer(struct fb_info *fb_info); ++extern int fbtft_unregister_framebuffer(struct fb_info *fb_info); ++extern void fbtft_register_backlight(struct fbtft_par *par); ++extern void fbtft_unregister_backlight(struct fbtft_par *par); ++extern int fbtft_init_display(struct fbtft_par *par); ++extern int fbtft_probe_common(struct fbtft_display *display, ++ struct spi_device *sdev, struct platform_device *pdev); ++extern int fbtft_remove_common(struct device *dev, struct fb_info *info); ++ ++/* fbtft-io.c */ ++extern int fbtft_write_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_spi_emulate_9(struct fbtft_par *par, ++ void *buf, size_t len); ++extern int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio8_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr(struct fbtft_par *par, void *buf, size_t len); ++extern int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, ++ void *buf, size_t len); ++ ++/* fbtft-bus.c */ ++extern int fbtft_write_vmem8_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus16(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus8(struct fbtft_par *par, size_t offset, size_t len); ++extern int fbtft_write_vmem16_bus9(struct fbtft_par *par, size_t offset, size_t len); ++extern void fbtft_write_reg8_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg8_bus9(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus8(struct fbtft_par *par, int len, ...); ++extern void fbtft_write_reg16_bus16(struct fbtft_par *par, int len, ...); ++ ++ ++#define FBTFT_REGISTER_DRIVER(_name, _compatible, _display) \ ++ \ ++static int fbtft_driver_probe_spi(struct spi_device *spi) \ ++{ \ ++ return fbtft_probe_common(_display, spi, NULL); \ ++} \ ++ \ ++static int fbtft_driver_remove_spi(struct spi_device *spi) \ ++{ \ ++ struct fb_info *info = spi_get_drvdata(spi); \ ++ \ ++ return fbtft_remove_common(&spi->dev, info); \ ++} \ ++ \ ++static int fbtft_driver_probe_pdev(struct platform_device *pdev) \ ++{ \ ++ return fbtft_probe_common(_display, NULL, pdev); \ ++} \ ++ \ ++static int fbtft_driver_remove_pdev(struct platform_device *pdev) \ ++{ \ ++ struct fb_info *info = platform_get_drvdata(pdev); \ ++ \ ++ return fbtft_remove_common(&pdev->dev, info); \ ++} \ ++ \ ++static const struct of_device_id dt_ids[] = { \ ++ { .compatible = _compatible }, \ ++ {}, \ ++}; \ ++ \ ++MODULE_DEVICE_TABLE(of, dt_ids); \ ++ \ ++ \ ++static struct spi_driver fbtft_driver_spi_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_spi, \ ++ .remove = fbtft_driver_remove_spi, \ ++}; \ ++ \ ++static struct platform_driver fbtft_driver_platform_driver = { \ ++ .driver = { \ ++ .name = _name, \ ++ .owner = THIS_MODULE, \ ++ .of_match_table = of_match_ptr(dt_ids), \ ++ }, \ ++ .probe = fbtft_driver_probe_pdev, \ ++ .remove = fbtft_driver_remove_pdev, \ ++}; \ ++ \ ++static int __init fbtft_driver_module_init(void) \ ++{ \ ++ int ret; \ ++ \ ++ ret = spi_register_driver(&fbtft_driver_spi_driver); \ ++ if (ret < 0) \ ++ return ret; \ ++ return platform_driver_register(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++static void __exit fbtft_driver_module_exit(void) \ ++{ \ ++ spi_unregister_driver(&fbtft_driver_spi_driver); \ ++ platform_driver_unregister(&fbtft_driver_platform_driver); \ ++} \ ++ \ ++module_init(fbtft_driver_module_init); \ ++module_exit(fbtft_driver_module_exit); ++ ++ ++/* Debug macros */ ++ ++/* shorthand debug levels */ ++#define DEBUG_LEVEL_1 DEBUG_REQUEST_GPIOS ++#define DEBUG_LEVEL_2 (DEBUG_LEVEL_1 | DEBUG_DRIVER_INIT_FUNCTIONS | DEBUG_TIME_FIRST_UPDATE) ++#define DEBUG_LEVEL_3 (DEBUG_LEVEL_2 | DEBUG_RESET | DEBUG_INIT_DISPLAY | DEBUG_BLANK | DEBUG_REQUEST_GPIOS | DEBUG_FREE_GPIOS | DEBUG_VERIFY_GPIOS | DEBUG_BACKLIGHT | DEBUG_SYSFS) ++#define DEBUG_LEVEL_4 (DEBUG_LEVEL_2 | DEBUG_FB_READ | DEBUG_FB_WRITE | DEBUG_FB_FILLRECT | DEBUG_FB_COPYAREA | DEBUG_FB_IMAGEBLIT | DEBUG_FB_BLANK) ++#define DEBUG_LEVEL_5 (DEBUG_LEVEL_3 | DEBUG_UPDATE_DISPLAY) ++#define DEBUG_LEVEL_6 (DEBUG_LEVEL_4 | DEBUG_LEVEL_5) ++#define DEBUG_LEVEL_7 0xFFFFFFFF ++ ++#define DEBUG_DRIVER_INIT_FUNCTIONS (1<<3) ++#define DEBUG_TIME_FIRST_UPDATE (1<<4) ++#define DEBUG_TIME_EACH_UPDATE (1<<5) ++#define DEBUG_DEFERRED_IO (1<<6) ++#define DEBUG_FBTFT_INIT_FUNCTIONS (1<<7) ++ ++/* fbops */ ++#define DEBUG_FB_READ (1<<8) ++#define DEBUG_FB_WRITE (1<<9) ++#define DEBUG_FB_FILLRECT (1<<10) ++#define DEBUG_FB_COPYAREA (1<<11) ++#define DEBUG_FB_IMAGEBLIT (1<<12) ++#define DEBUG_FB_SETCOLREG (1<<13) ++#define DEBUG_FB_BLANK (1<<14) ++ ++#define DEBUG_SYSFS (1<<16) ++ ++/* fbtftops */ ++#define DEBUG_BACKLIGHT (1<<17) ++#define DEBUG_READ (1<<18) ++#define DEBUG_WRITE (1<<19) ++#define DEBUG_WRITE_VMEM (1<<20) ++#define DEBUG_WRITE_REGISTER (1<<21) ++#define DEBUG_SET_ADDR_WIN (1<<22) ++#define DEBUG_RESET (1<<23) ++#define DEBUG_MKDIRTY (1<<24) ++#define DEBUG_UPDATE_DISPLAY (1<<25) ++#define DEBUG_INIT_DISPLAY (1<<26) ++#define DEBUG_BLANK (1<<27) ++#define DEBUG_REQUEST_GPIOS (1<<28) ++#define DEBUG_FREE_GPIOS (1<<29) ++#define DEBUG_REQUEST_GPIOS_MATCH (1<<30) ++#define DEBUG_VERIFY_GPIOS (1<<31) ++ ++ ++#define fbtft_init_dbg(dev, format, arg...) \ ++do { \ ++ if (unlikely((dev)->platform_data && \ ++ (((struct fbtft_platform_data *)(dev)->platform_data)->display.debug & DEBUG_DRIVER_INIT_FUNCTIONS))) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg(level, par, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(par->info->device, format, ##arg); \ ++} while (0) ++ ++#define fbtft_dev_dbg(level, par, dev, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ dev_info(dev, format, ##arg); \ ++} while (0) ++ ++#define fbtft_par_dbg_hex(level, par, dev, type, buf, num, format, arg...) \ ++do { \ ++ if (unlikely(par->debug & level)) \ ++ fbtft_dbg_hex(dev, sizeof(type), buf, num * sizeof(type), format, ##arg); \ ++} while (0) ++ ++#endif /* __LINUX_FBTFT_H */ +diff --git a/drivers/video/fbtft/fbtft_device.c b/drivers/video/fbtft/fbtft_device.c +new file mode 100644 +index 0000000..b9f4c30 +--- /dev/null ++++ b/drivers/video/fbtft/fbtft_device.c +@@ -0,0 +1,1444 @@ ++/* ++ * ++ * 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 }; ++ ++static int waveshare32b_init_sequence[] = { ++ -1,0xCB,0x39,0x2C,0x00,0x34,0x02,-1,0xCF,0x00,0xC1,0x30, ++ -1,0xE8,0x85,0x00,0x78,-1,0xEA,0x00,0x00,-1,0xED,0x64,0x03,0x12,0x81, ++ -1,0xF7,0x20,-1,0xC0,0x23,-1,0xC1,0x10,-1,0xC5,0x3e,0x28,-1,0xC7,0x86, ++ -1,0x36,0x28,-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,120,-1,0x29,-1,0x2c,-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 = "adafruit28", ++ .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 = "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 = "agm1264k-fl", ++ .pdev = &(struct platform_device) { ++ .name = "fb_agm1264k-fl", ++ .id = 0, ++ .dev = { ++ .release = fbtft_device_pdev_release, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = FBTFT_ONBOARD_BACKLIGHT, ++ }, ++ .gpios = (const struct fbtft_gpio []) { ++ {}, ++ }, ++ }, ++ } ++ } ++ }, { ++ .name = "dogs102", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_uc1701", ++ .max_speed_hz = 8000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 13 }, ++ { "dc", 6 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = "ili9481", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 32000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .regwidth = 16, ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 25 }, ++ { "dc", 24 }, ++ { "led", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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 = { ++ .regwidth = 16, ++ .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 = "tontec35_9481", /* boards before 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9481", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "led_", 18 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .name = "tontec35_9486", /* boards after 02 July 2014 */ ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9486", ++ .max_speed_hz = 128000000, ++ .mode = SPI_MODE_3, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 15 }, ++ { "dc", 25 }, ++ { "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 = "waveshare32b", ++ .spi = &(struct spi_board_info) { ++ .modalias = "fb_ili9340", ++ .max_speed_hz = 48000000, ++ .mode = SPI_MODE_0, ++ .platform_data = &(struct fbtft_platform_data) { ++ .display = { ++ .buswidth = 8, ++ .backlight = 1, ++ .init_sequence = waveshare32b_init_sequence, ++ }, ++ .bgr = true, ++ .gpios = (const struct fbtft_gpio []) { ++ { "reset", 27 }, ++ { "dc", 22 }, ++ {}, ++ }, ++ } ++ } ++ }, { ++ .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/u-boot/u-boot/add-cubieboard4.patch b/patch/u-boot/u-boot/add-cubieboard4.patch new file mode 100644 index 000000000..326415f00 --- /dev/null +++ b/patch/u-boot/u-boot/add-cubieboard4.patch @@ -0,0 +1,21 @@ +diff --git a/arch/arm/dts/Cubieboard4_defconfig b/arch/arm/dts/Cubieboard4_defconfig +new file mode 100755 +index 0000000..f400ed2 +--- /dev/null ++++ b/arch/arm/dts/Cubieboard4_defconfig +@@ -0,0 +1,15 @@ ++CONFIG_ARM=y ++CONFIG_ARCH_SUNXI=y ++CONFIG_MACH_SUN9I=y ++CONFIG_DRAM_CLK=360 ++CONFIG_DRAM_ZQ=123 ++CONFIG_SYS_CLK_FREQ=1008000000 ++CONFIG_MMC0_CD_PIN="PH18" ++# CONFIG_VIDEO is not set ++CONFIG_DEFAULT_DEVICE_TREE="sun9i-a80-cubieboard4" ++# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set ++# CONFIG_CMD_IMLS is not set ++# CONFIG_CMD_FLASH is not set ++# CONFIG_CMD_FPGA is not set ++CONFIG_CMD_GPIO=y ++CONFIG_SYS_NS16550=y